Note: Previous guides are now deprecated due to updated packaging, and other minor changes to Ubuntu or Invoice Ninja

Install Invoice Ninja 5 on Ubuntu 24.04 LTS

Ubuntu and debian based distros are highly reccomended for running the InvoiceNinja software. Other distros like RHEL based, or even Arch are compatible with this software, and this guide might help you determine how to set that up, but I cannot support those installations in this guide.

First: Install dependencies

Invoice Ninja is built on PHP 8.3, so we must first install that, and the recommended extensions for it. We can also install MariaDB for SQL service, NGINX will be used for webhosting in my tutorial, and vim as tools for later steps.

As of 11/13/2024 this guide was updated to include adding the third party repository below ppa:ondrej/php in order to install php8.3

$ sudo apt update
$ sudo apt dist-upgrade -y

$ sudo apt update

$ sudo apt install php-common php-bcmath php-gd php-mbstring php-xml php-cli php-curl php-zip php-gmp php-mysql php-fpm

$ sudo apt install mariadb-server mariadb-client nginx vim

Second: Configure MariaDB

Start, and enable services for mariadb, the program/service that manages your SQL database and the incoming and outgoing communication it has with other applications like Invoice Ninja.

$ sudo systemctl enable --now mariadb

This command will take you through a guided wizard to initialize the SQL database. Remember Make sure to change your SQL root password, and save that password somewhere safe and secure, to protect access to the databases.

$ sudo mysql_secure_installation

Enter current password for root (enter for none): 

Switch to unix_socket authentication [Y/n] n

Change the root password [Y/n] y

Remove anonymous users? [Y/n] y

Disallow root login remotely? [Y/n] y

Remove test database and access to it? [Y/n] y

Reload privilege tables now? [Y/n] y

These commands will directly access the SQL database through the services provided by mariadb. We will create a database with any arbitrary name ‘ninjadb’ in this example, and create arbitrary username and password combination ‘ninja’ and ‘ninjapass’. The database name will be used by InvoiceNinja during the server setup after installation is complete, as well as the username and password you specify here, in order for InvoiceNinja to login to the SQL database with read/write permission.

$ sudo mysql -u root -p
Enter Password:  ******
MariaDB .. > create database invoicedb;
MariaDB .. > create user 'ninja'@'localhost' identified by 'ninjapass';
MariaDB .. > grant all privileges on invoicedb.* to 'ninja'@'localhost';
MariaDB .. > flush privileges;
MariaDB .. > exit

Third: Installing Invoice Ninja

Installing the application files in the NGINX web server directory.

Please visit https://github.com/invoiceninja/invoiceninja/releases to get the latest github release of InvoiceNinja from the team. Optionally dropping to root (sudo bash) will help us navigate the folder structure without permission issues.

  $ cd /usr/share/nginx
  $ sudo mkdir invoiceninja && cd invoiceninja
  $ sudo wget <latest invoiceninja.tar url link>
  $ sudo tar -xvf invoiceninja.tar
  $ sudo rm invoiceninja.tar

Populate .env file

The Invoice Ninja package doesn’t contain a .env file, so it won’t overwrite your existing file when you update. It does include a .env.example file. Note, that the .env file will automatically be populated with

 $ sudo cp .env.example .env

Optionally backup your .env file

  $ sudo cp .env /path/to/backup/.env.bak

Prepare Invoice Ninja permissions

Set permissions for the directory and all its contents to allow web server permission to view and edit files. The default user for Ubuntu and debian based distros is www-data.

  $ sudo chown -R www-data:www-data /usr/share/nginx/invoiceninja

Enable Cron job automation

Now, there is need to enable cron job that will run some sort of regular maintenance, or you get a nasty red exclamation mark error in InvoiceNinja after logging in. See here for more: https://invoiceninja.github.io/selfhost.html#installing-invoice-ninja

  $ sudo -u www-data crontab -e

Then copy paste the following into the bottom of your cron file, which will be run for something to do with laravel which InvoiceNinja depends on.

This will run, as user www-data, with the explicitly correct php version, the artisan schedule command in order to provide Invoice Ninja application with the backend services it needs to work properly. Edit the path according to your environment, also, specifying which version of php to use in the crontab, and other places, ensures we don’t inadvertently use the wrong version on a system with multiple versions of php running.

  * * * * * php /usr/share/nginx/invoiceninja/artisan schedule:run >> /dev/null 2>&1

Manually Update MariaDB config in .env

During the webgui setup step for Invoice Ninja, sometimes you may pass the check to validate your db credentials, but you will get an ERROR 500 afterwards, and if you check storage/logs/laravel.log you might see log messages about failures to connect to the db.

In this case, manually update the .env file to explicitly use the credentials, because the setup might not be saving your db name and credentials to the .env file before running actual setup with laravel.

Manually enter the database name, username and password, and if applicable, host address and port for highly customized setups. These were setup above in step 2 - Configure MariaDB.

  DB_HOST="localhost"
  DB_DATABASE="invoicedb"
  DB_USERNAME="ninja"
  DB_PASSWORD="ninjapass"
  DB_PORT="3306"

Then try to run the webgui setup again. It should pass and successfully populate the database after this. You can further customize your .env as you see fit including the mail server settings, and any API keys you use.

Fourth: Configure NGINX

FOR TESTING - NOT for Production use - OpenSSL certification

You should not need to do this. I am about to show you NGINX configuration file that points to an example based openssl cert. Most of you would be using letsencrypt, or some other CA. I am not going to provide a guide to setup letsencrypt. So for the purposes of this guide, the NGINX configuration will use an instant OpenSSL certificate that is not particularly trusted on the Internet.

$ sudo mkdir -p /etc/nginx/cert
$ sudo openssl req -new -x509 -days 365 -nodes -out /etc/nginx/cert/ninja.crt -keyout /etc/nginx/cert/ninja.key

Secure NGINX

The default NGINX install on Ubuntu has a pesky default website located at /etc/nginx/sites-enabled/default - and for our cases, we do not want this default website hosted by nginx. When left unconfigured, this page presents some security loopholes. It can also cause conflicts sometimes. Lets remove it.

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

NGINX configuration page for website

Create the text configuration file for a new vhost in the sites-available folder, best practice if you host multiple websites or webapps from a single server.

$ sudo vim /etc/nginx/sites-available/invoiceninja.mysite.com

Press ‘i’ to enter insert mode, and paste this server configuration.

Review it line by line, and edit the server name, root path, ssl path, php-fpm socket path, etc, as necessary.

You cannot copy paste this entire document, you will need to edit appropriate sections to accomodate your own environment. I will try to “”“indicate”“” when specific strings of text need your attention.

NOTE this guide uses a standard self-signed cert for SSL, so in the NGINX config sample file below, you will need to edit the lines for the path of an example of a self-signed cert, to instead point at your own real certs.

  server {
# NOTE That the 'default_server' option is only necessary if this is your primary domain application.
# If you run multiple subdomains from the same host already, remove the 'default_server' option.
   listen       443 ssl http2 default_server;
   listen       [::]:443 ssl http2 default_server;
   server_name  """invoices.example.ca""";
   client_max_body_size 20M;

 # This if statement will forcefully redirect any connection attempts to explicitly use the domain name.  
 # If not, and your DNS doesn't provide IP address protection, accessing the server with direct IP can
 # cause glitches with the services provided by the app, that could be security, or usability issues.

   if ($host != $server_name) {
     return 301 https://$server_name$request_uri;
   }

 # Here, enter the path to your invoiceninja directory, in the public dir.  VERY IMPORTANT
 # DO NOT point the root directly at your invoiceninja directory, it MUST point at the public folder
 # This is for security reasons.

   root         /usr/share/nginx/"""invoiceninja"""/public;

   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;

   index index.php index.html index.htm;

 # Enter the path to your existing ssl certificate file, and certificate private key file
 # If you don’t have one yet, you can configure one with openssl in the next step.
   ssl_certificate "/etc/nginx/cert/ninja.crt";
   ssl_certificate_key "/etc/nginx/cert/ninja.key";

   ssl_session_cache shared:SSL:1m;
   ssl_session_timeout  10m;
   ssl_ciphers 'AES128+EECDH:AES128+EDH:!aNULL';
   ssl_prefer_server_ciphers on;
   ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

   charset utf-8;

 # Load configuration files for the default server block.
   include /etc/nginx/default.d/*.conf;

   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)(/.+)$;
      # Here we pass php requests to the php-fpm listen socket.  
      # PHP errors are often because this value is not correct.  
      # Verify your php-fpm.sock socket file exists at the below directory
      # and that the php-fpm service is running.
           fastcgi_pass unix:/run/php/php-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;
   }

   location ~ /\.ht {
       deny all;
   }

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

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

   sendfile off;

  }

  server {
      listen      80;
      server_name """invoices.example.ca""";
      add_header Strict-Transport-Security max-age=2592000;
      rewrite ^ https://$server_name$request_uri? permanent;
  }

Make sure you change any variables for your environment, “”“recommended by use of quotations”“”.

Create a symlink

In for NGINX to use the vhost file you just created, you must setup a symlink to the sites-enabled folder, note that you must also use a full, explicit directory path and not a relative path for this command, or you will encounter an error:

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

Verify your NGINX configuration with the following command, and troubleshoot any errors on the lines it specifies for you;

 $ sudo nginx -t

Disable & stop Apache2. Start & enable NGINX.

For Ubuntu Server edition, you might have apache2 installed and enabled by defualt. Follow the steps below to disable apache2, and enable nginx instead for webhosting service.

  $ sudo systemctl stop apache2
  $ sudo systemctl disable apache2
  $ sudo systemctl enable nginx
  $ sudo systemctl start nginx

Fin

Now that your website is ready, you can run through the web-based setup of your company, by visiting the initial setup page:

https://invoiceninja.mysite.com/setup

BACKUP FILES & DATA

As of Ubuntu 24.04 LTS at least, Ubuntu is now using at minimum, a compatible version of php with Invoice Ninja, and you no longer need to explicitly specify which php or php-fpm version you are using. Use standard php packages as per this guide, and get them from the main Ubuntu repositories, no need for 3rd party repos anymore.

The following line in the NGINX config file for Invoice Ninja governs which php-fpm socket is used:

 fastcgi_pass unix:/run/php/php-fpm.sock;

If you are using an explicit version of php or php-fpm (for example php8.3-fpm) you may have a different path - also if you are on a different distro, such as an RHEL or Arch based distro, you might have a different path to your php-fpm.sock file also. If you run into issues, validate the path to php-fpm.sock on your local system, and update your nginx configuration to match that.