Deploying Invoice Ninja on Ubuntu 24.04

Invoice Ninja is an open-source invoicing platform built on Laravel that helps businesses send invoices and track payments. It provides a free alternative to popular accounting solutions like FreshBooks and QuickBooks. With Invoice Ninja, you can handle financial tasks such as managing income, expenses, payments, and even time tracking.

This guide explains how to deploy Invoice Ninja on an Ubuntu 24.04 server. You will install and configure the application so that it can be used for financial management directly on your server.

Prerequisites

Before starting, make sure you have the following:

  • An Ubuntu 24.04 server instance with a non-root user that has sudo privileges.
  • A configured domain A record pointing to your server’s IP address, for example: invoiceninja.example.com.

Installing Invoice Ninja

Invoice Ninja can be installed using the latest release package or Docker. Using the release file provides immediate access to new features, while Docker simplifies deployment by avoiding manual package installations. The following steps explain how to install Invoice Ninja using the release file method on your server.

Step 1: Update System Packages

Step 2: Configure Firewall

Allow HTTP and HTTPS traffic through your firewall:

$ sudo ufw allow http && sudo ufw allow https

Step 3: Install Required Dependencies

Invoice Ninja requires PHP 8.3 and a LAMP or LEMP stack. First, install Nginx, MySQL, and PHP:

$ sudo apt install nginx mysql-server php -y

Check that the installed PHP version is 8.3 or newer:

Now install PHP-FPM and the required extensions:

$ sudo apt install php8.3-bcmath php8.3-gmp php8.3-fileinfo \
    php8.3-gd php8.3-mbstring php8.3-pdo php8.3-xml php8.3-cli \
    php8.3-curl php8.3-zip php8.3-gmp php8.3-mysql php8.3-fpm -y

Enable and start the Nginx service:

$ sudo systemctl enable nginx
$ sudo systemctl start nginx
$ sudo systemctl status nginx

Create MySQL Database

Invoice Ninja requires a MySQL database. Follow these steps:

Log in as root:

Create the database and user:

mysql> CREATE DATABASE invoiceninjadb;
mysql> CREATE USER 'invoiceninja-admin'@'localhost' IDENTIFIED BY 'secure-password';
mysql> GRANT ALL PRIVILEGES ON invoiceninjadb.* TO 'invoiceninja-admin'@'localhost';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

Download and Configure Invoice Ninja

Create the application directory and move into it:

$ sudo mkdir -p /var/www/invoiceninja
$ cd /var/www/invoiceninja

Download and extract the latest release (example version 5.11.72):

$ sudo wget https://github.com/invoiceninja/invoiceninja/releases/download/v5.11.72/invoiceninja.tar.gz
$ sudo tar -xvf invoiceninja.tar.gz
$ sudo rm invoiceninja.tar.gz

Set up environment file and permissions:

$ sudo cp .env.example .env
$ sudo chown -R www-data:www-data /var/www/invoiceninja

Configure Cron Jobs

Open the crontab as www-data user:

$ sudo -u www-data crontab -e

Add the following Laravel scheduling command:

* * * * * php8.3 /var/www/invoiceninja/artisan schedule:run >> /dev/null 2>&1

Verify that the cron task is active:

$ sudo -u www-data crontab -l

Output:

# m h  dom mon dow   command
* * * * * php8.3 /var/www/invoiceninja/artisan schedule:run >> /dev/null 2>&1

Configure Invoice Ninja

To run Invoice Ninja, you need to configure a virtual host in Nginx so that all files inside the /var/www/invoiceninja directory are properly served. Follow these steps to create and configure a new virtual host.

Step 1: Create a Virtual Host File

Create a new configuration file named invoiceninja.conf in the /etc/nginx/sites-available directory:

$ sudo nano /etc/nginx/sites-available/invoiceninja.conf

Add the following Nginx server block (replace invoiceninja.example.com with your actual domain):

server {
    listen 80;
    listen [::]:80;
    server_name invoiceninja.example.com;

    root /var/www/invoiceninja/public;
    index index.php index.html index.htm;

    client_max_body_size 20M;
    charset utf-8;

    access_log /var/log/nginx/ininja.access.log;
    error_log /var/log/nginx/ininja.error.log;

    gzip on;
    gzip_types application/javascript application/x-javascript text/javascript text/plain application/xml application/json;
    gzip_proxied no-cache no-store private expired auth;
    gzip_min_length 1000;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    if (!-e $request_filename) {
        rewrite ^(.+)$ /index.php?q= last;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }
}

This virtual host configuration ensures that Invoice Ninja files in the /var/www/invoiceninja/public directory are served under your domain, and all PHP requests are processed by PHP 8.3 FPM.

Step 2: Enable the Virtual Host

Remove the default Nginx virtual host configuration:

$ sudo rm /etc/nginx/sites-enabled/default

Activate the new virtual host by linking it into the sites-enabled directory:

$ sudo ln -s /etc/nginx/sites-available/invoiceninja.conf /etc/nginx/sites-enabled/invoiceninja.conf

Test the Nginx configuration for syntax errors:

Output:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart Nginx to apply the changes:

$ sudo systemctl restart nginx

Secure Invoice Ninja with SSL

Next, secure Invoice Ninja with Let’s Encrypt SSL certificates.

Install the Certbot plugin for Nginx:

$ sudo apt install python3-certbot-nginx

Request a new TLS certificate. Replace invoiceninja.example.com with your domain and admin@example.com with your email:

$ sudo certbot --nginx -d invoiceninja.example.com -m admin@example.com --agree-tos

Restart Nginx to load the new SSL configuration:

$ sudo systemctl restart nginx

Set Up Invoice Ninja

Now that your server is configured, you can finish setting up Invoice Ninja in a web browser. Open the setup page:

https://invoiceninja.example.com/setup

The setup wizard will guide you through the configuration:

  • Enter your domain in the URL field and leave the HTTPS Require option enabled.
  • Confirm that MySQL is selected as the database driver.
  • Keep localhost as the host (or specify another if needed).
  • Keep port 3306 unless your database is configured differently.
  • Provide the database name, username, and password you created earlier.
  • Click Test connection to ensure the database settings are correct.
  • Fill in your administrator details in the User Details form.
  • Accept the terms of service and privacy policy.
  • Click Submit to complete the setup.

Install Invoice Ninja with Docker Compose

You can also install Invoice Ninja using Docker Compose. This method deploys Invoice Ninja together with all required services like Nginx, MariaDB, and Redis. Follow the steps below to complete the installation.

Step 1: Install Docker and Dependencies

Install Docker, Docker Compose, and Certbot with the Nginx plugin:

$ sudo apt install docker.io docker-compose certbot python3-certbot-nginx -y

Remove Nginx to free up port 80:

Step 2: Prepare the Project Directory

Switch to your home directory and create the project structure:

$ cd
$ mkdir invoiceninja
$ cd invoiceninja
$ mkdir -p storage config public

Change the permissions for storage and public directories so that the container can write to them:

$ sudo chmod -R 777 storage public

Step 3: Generate an API Key

Generate a new API key for Invoice Ninja:

$ sudo docker run --rm -it invoiceninja/invoiceninja php artisan key:generate --show

Copy the generated key, for example:

base64:3OoSrTSLswQyfY/NjMvVaxoODn/NyFzzwwkjfou6JuQ=

Step 4: Create docker-compose.yml

Create a new Docker Compose file:

Paste the following configuration and adjust the values as explained below:

version: '3.7'

services:
  nginx:
    image: nginx
    restart: unless-stopped
    volumes:
      - ./config/in-vhost.conf:/etc/nginx/conf.d/in-vhost.conf:ro
      - ./public:/var/www/app/public:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - app
    ports:
      - "80:80"
      - "443:443"
    healthcheck:
      test: curl -f http://localhost:80/ || exit 1
    networks:
      - invoice_ninja_network

  db:
    image: mariadb:10.4
    restart: unless-stopped
    volumes:
      - ./data:/var/lib/mysql:rw,delegated
    environment:
      MARIADB_PASSWORD: ninja
      MARIADB_ROOT_PASSWORD: ninja
      MARIADB_DATABASE: ninja
      MARIADB_USER: ninja
      PUID: "1026"
      PGID: "100"
    networks:
      - invoice_ninja_network

  cache:
    image: redis
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping || exit 1"]
    networks:
      - invoice_ninja_network

  app:
    image: invoiceninja/invoiceninja:5
    restart: unless-stopped
    volumes:
      - ./public:/var/www/app/public:rw,delegated
      - ./storage:/var/www/app/storage:rw,delegated
    depends_on:
      db:
        condition: service_started
      cache:
        condition: service_healthy
    environment:
      APP_NAME: "Invoice Ninja"
      APP_ENV: "production"
      APP_KEY: base64:3OoSrTSLswQyfY/NjMvVaxoODn/NyFzzwwkjfou6JuQ=  # Replace with your key
      APP_DEBUG: "0"
      APP_URL: "https://invoiceninja.example.com"  # Replace with your domain
      IS_DOCKER: "true"
      PHANTOMJS_PDF_GENERATION: "0"
      PDF_GENERATOR: "snappdf"
      TRUSTED_PROXIES: "*"
      MULTI_DB_ENABLED: "0"
      DB_HOST: db
      DB_DATABASE: ninja
      DB_USERNAME: ninja
      DB_PASSWORD: ninja
      DB_PORT: "3306"
      PUID: "1026"
      PGID: "100"
      CACHE_DRIVER: redis
      SESSION_DRIVER: redis
      REDIS_HOST: cache
      MAIL_MAILER: smtp
      MAIL_HOST: smtp.ionos.de
      MAIL_PORT: "465"
      MAIL_USERNAME: xxx@yyyy
      MAIL_PASSWORD: XXX
      MAIL_ENCRYPTION: SSL
      MAIL_FROM_ADDRESS: "invoiceninja@example.com"
      MAIL_FROM_NAME: "Max Crisp"
      REQUIRE_HTTPS: "1"
      NINJA_ENVIRONMENT: "selfhost"
      IN_USER_EMAIL: admin@example.com
      IN_PASSWORD: GEHEIM
    networks:
      - invoice_ninja_network

networks:
  invoice_ninja_network:
    driver: bridge

Make sure to replace:

  • APP_KEY with the generated API key.
  • APP_URL with your domain.
  • MARIADB_* values with your own database settings.
  • DB_* values in the app service with those from the db section.
  • MAIL_* values with your mail server details if used.
  • IN_USER_EMAIL and IN_PASSWORD with your login credentials.

Step 5: Generate SSL Certificates

Generate a trusted TLS certificate for your domain:

$ sudo certbot certonly --standalone -d invoiceninja.example.com --email invoiceninja@example.com --agree-tos --non-interactive

The certificates are stored in /etc/letsencrypt/live/invoiceninja.example.com/. These paths are used in the virtual host configuration.

Step 6: Configure Nginx Virtual Host

Create the Nginx configuration file in the config directory:

$ nano config/in-vhost.conf

Add the following configuration (replace domain values with your own):

server {
    listen 80 default_server;
    server_name invoiceninja.example.com;  # Replace with your domain

    # Redirect all HTTP traffic to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl default_server;
    server_name invoiceninja.example.com;  # Replace with your domain

    server_tokens off;
    client_max_body_size 100M;

    # SSL certificate paths
    ssl_certificate /etc/letsencrypt/live/invoiceninja.example.com/fullchain.pem;  # Replace with your domain
    ssl_certificate_key /etc/letsencrypt/live/invoiceninja.example.com/privkey.pem;  # Replace with your domain

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;

    root /var/www/app/public/;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    location ~* /storage/.*\.php$ {
        return 503;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_intercept_errors off;
        fastcgi_buffer_size 16k;
        fastcgi_buffers 4 16k;
    }
}

Step 7: Start Containers

Start all services in detached mode:

$ sudo docker-compose up -d

Verify that all containers are running:

$ sudo docker container ls

Output example:

CONTAINER ID   IMAGE                         COMMAND                  CREATED         STATUS                   PORTS                                                                      NAMES
2aeefe15c99c   nginx                         "/docker-entrypoint.…"   3 minutes ago   Up 2 minutes (healthy)   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   invoiceninja_nginx_1
9fc27c288ac4   invoiceninja/invoiceninja:5   "docker-entrypoint s…"   3 minutes ago   Up 3 minutes             9000/tcp                                                                   invoiceninja_app_1
fa2e661a93ab   redis                         "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes (healthy)   6379/tcp                                                                   invoiceninja_cache_1
0a27ba745af1   mariadb:10.4                  "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes             3306/tcp                                                                   invoiceninja_db_1

Step 8: Enable Automatic Certificate Renewal

Edit the system crontab file:

Add the following line to renew the SSL certificate automatically every day:

0 0 * * * root certbot renew --quiet

This ensures your TLS certificates remain valid without manual intervention.

Access and Use Invoice Ninja

Once the installation is complete, you can access and start using Invoice Ninja by following these steps:

Step 1: Log In

Open the login page in your browser using your configured domain:

https://invoiceninja.example.com/login

Enter your administrator email and password, then click Login to access the Invoice Ninja dashboard.

Step 2: Initial Setup

When prompted with the Welcome to Invoice Ninja screen:

  • Enter your company name (e.g., example-company).
  • Select the default currency for your invoices.

Step 3: Configure Company Information

Navigate to Settings and complete your company details such as address, tax number, and other relevant information.

Step 4: Create a New Invoice

From the main navigation menu, select Invoices and click New Invoice.

  • Click New Client, add the client’s details, and click Save.
  • Specify invoice details including date, due date, invoice number, PO number, and discounts.
  • Click Add Item under the Products section to include new items in the invoice.
  • Click New Product, enter the product details, and click Save.
  • Provide unit cost and quantity information.

Step 5: Add Additional Information

Navigate through the Public Notes, Terms, and Footer sections to add extra details to your invoice.

Step 6: Review and Save

Scroll through the invoice preview to verify all information is correct.

  • You can download or print the invoice directly from the preview screen.
  • Click Save to store the invoice in your records.

Conclusion

In this tutorial, you learned how to deploy Invoice Ninja on Ubuntu 24.04 using both the official package and Docker. With Invoice Ninja, you can create invoices, manage clients, and maintain financial records easily from the dashboard.

For further details, check the official Invoice Ninja documentation.

Source: vultr.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: