Install Invoice Ninja v5 on Debian 11 Bullseye

Edit: Appereantly Ubuntu is preferred over Debian for InvoiceNinja. Please switch to this repo

Hi everyone

I wrote a basic tutorial for Debian and Ninja v5.
I only tested this behind a reverse proxy and would love to hear some feedback from you.
Also the optional supervisor part is not done yet.

I like this forum but I think github is better suited to open bug reports than here and it is simpler for me to keep it up to date. Link to Github

I am not a native English speaker and will reread my horrible spelling, but not today :wink:

# This is a basic tutorial for a

# default installation of a InvoiceNinja instance on Debian

# You can also find this and report a bug on

# I did a mostly default Debian installation

# no password for root, and invoiceninja as a user. That way invoiceninja is member of the sudo group

# selected ssh server to be installed. This is optional.

# if you set a password for the root user, install sudo and add user invoiceninja to sudoer group. Otherwise you can skip this.

su -

apt install sudo

usermod -a -G sudo invoiceninja

# I import my public ssh keys from github. This step is optional.

sudo apt install ssh-import-id

ssh-import-id-gh HereYourUsername

# You can skip this, if you don't use ssh server. Otherwise I would recommend disabling PasswordAuth for ssh.

# uncomment by removing # and change PasswordAuhtentication from yes to no

sudo nano /etc/ssh/sshd_config

# I like to have security updates automatically installed. This is optional.

sudo apt install unattended-upgrades

sudo systemctl enable --now unattended-upgrades

# install dependencies

# Debian 11 Bullseye currently uses php 7.4

# do we really need curl, git, vim, maridb-client?

sudo apt install php7.4 php7.4-{fpm,bcmath,ctype,fileinfo,json,mbstring,pdo,tokenizer,xml,curl,zip,gmp,gd,mysqli} mariadb-server mariadb-client curl git nginx vim composer -y

# if we visit http://yourIP , you should see a running NGINX page. https://yourIP will not work you need to use http instead of https for now!

# delete the default site

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

# Check if php-fpm is running. You should see something like active: active (running)

systemctl status php7.4-fpm

# press q to quit

# enable mariadb

sudo systemctl enable --now mariadb

# This command will take you through a guided wizard to initialize the SQL database. Use default values written in capital letters

sudo mysql_secure_installation

# Enter, Enter, Enter, insert a password for the root account, enter it again, Enter, Enter, Enter, Enter. You should see "Thanks for using MariaDB"

# login to the database

mysql -u root -p

# enter the password you just set

# you should see MariaDB [(none)]> on the left of your cursor

# create database ninjadb


# You should see Query OK, 1 row affected

# create the user ninja and set a Password

CREATE USER 'ninja'@'localhost' IDENTIFIED BY 'Password';

# You should see Query OK, 0 rows affected

# give all permissions for to the local ninja user to access ninjadb

GRANT ALL PRIVILEGES ON ninjadb.* TO 'ninja'@'localhost';

# You should see Query OK, 0 rows affected

# reload privileges


# You should see Query OK, 0 rows affected

# exit the db


# You should see Bye


# create config for webpage

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

# insert this:


# You should look at the following URL's in order to grasp a solid understanding

# of Nginx configuration files in order to fully unleash the power of Nginx.





# In most cases, administrators will remove this file from sites-enabled/ and

# leave it as reference inside of sites-available where it will continue to be

# updated by the nginx packaging team.


# This file will automatically load configuration files provided by other

# applications, such as Drupal or Wordpress. These applications will be made

# available underneath a path with that package name, such as /drupal8.


# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.


# Default server configuration


server {

        listen 80 default_server;

        listen [::]:80 default_server;

        # SSL configuration


        # listen 443 ssl default_server;

        # listen [::]:443 ssl default_server;


        # Note: You should disable gzip for SSL traffic.

        # See:


        # Read up on ssl_ciphers to ensure a secure configuration.

        # See:


        # Self signed certs generated by the ssl-cert package

        # Don't use them in a production server!


        # include snippets/snakeoil.conf;

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

        # Add index.php to the list if you are using PHP

        index index.html index.htm index.php index.nginx-debian.html;

        charset utf-8;

        client_max_body_size 20M;

        #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;        

        server_name _;

        location / {

                # First attempt to serve request as file, then

                # as directory, then fall back to displaying a 404.

                try_files $uri $uri/ =404;


        # pass PHP scripts to FastCGI server


        location ~ \.php$ {

               include snippets/fastcgi-php.conf;


               # With php-fpm (or other unix sockets):

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

               # With php-cgi (or other tcp sockets):

        #       fastcgi_pass;


        # deny access to .htaccess files, if Apache's document root

        # concurs with nginx's one


        location ~ /\.ht {

               deny all;


        location ~* \.pdf$ {

                add_header Cache-Control no-store;


        if (!-e $request_filename) {

                rewrite ^(.+)$ /index.php?q= last;


        location = /favicon.ico {

                access_log off; log_not_found off;


        location = /robots.txt {

                access_log off; log_not_found off;



# save and exit

# If php7.4 is not the used php version anymore, you need to check the FastCGI socket path.

# As of march 2022, InvoiceNinja still uses php7.4 and you don't need to change anything.

# make sure NGINX is running and not apache

sudo systemctl stop apache2

sudo systemctl disable apache2

sudo systemctl enable --now nginx

# time for a break :) take a snapshot if possible

# make install dir

cd /usr/share/nginx

sudo mkdir invoiceninja && cd invoiceninja

# Find latest version here

# Right click the link to the zip file and copy the download link

# Download the zip

sudo wget

# unzip and remove the zip file

sudo unzip

sudo rm

# if you wanna use snappdf instead of the cloud service phantomPDF, we need some

# dependancies for snappdf

# more info

sudo apt install ca-certificates fonts-liberation libappindicator3-0.1-cil libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc-s1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils

# Copy the example .env

sudo cp /usr/share/nginx/invoiceninja/.env.example /usr/share/nginx/invoiceninja/.env

# Now we need to edit your .env file

# you only need to edit one line (or two if you have a revers proxy) everything else we can setup later in the webgui setup

sudo nano /usr/share/nginx/invoiceninja/.env

APP_NAME="Invoice Ninja"




























MAIL_FROM_NAME='Self Hosted User'





# If you have a reverse proxy you can set its ip here



# change this to snappdf

#options - snappdf / phantom / hosted_ninja





COMPOSER_AUTH='{"github-oauth": {"": "${{ secrets.GITHUB_TOKEN }}"}}'


# save and exit

# I take another break and make the second snapshot

# set permissions to the nginx user

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

# edit crontab to run Laravel Scheduler every minute

sudo -u www-data crontab -e

# insert this at the end:

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

# save and exit

# enable webpage

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

# test the invoiceninja.conf

# this should give you no error

sudo nginx -t

# I shutdown the VM before the webconfig and take a third snapshot

sudo shutdown

# The next steps are very much dependant if you use InvoiceNinja behind a proxy or not or if you only use invoiceninja local and tous do not need a cert.

# If you don't use it behind a proxy, you need to install certbot on the InvoiceNinja host.

# If you use it behind a proxy, you configure certbot on the proxy

# If you don't use https, you don't have to do anything

# visit our http://ipofyourhost/setup address to setup InvoiceNinja

# For URL you set the desired URL or the IP address if you use it local only

# HTTP should be require unless you use a reverse proxy

# Test PDF should show success

# Insert the database credentials

# localhost

# 3306

# ninjadb

# ninja

# The password you set during the DB setup

# Test PDF should show success

# If you wanna send mail, you can set SMTP instead of Log

# Example config for Office 365





# 587


# Password

# Send test email shoud work

# Create a User Account, agree to the terms and click submit

# Now this could take some time. You should be redirected to the URL you defined earlier.

# Don't leave the page and have some patience. If the page is just gray, try to disable pihole or any other adblockers

# Your browser redirect will probably point to https and because we have not setup nginx to listen on https yet the site is unreachable.

# In this case setup certbot first before you try to login.

# install and run certbot to get a cert

# install snapd

sudo apt install snapd

# update snapd

sudo snap install core

sudo snap refresh core

# install certbot

sudo snap install --classic certbot

# create a cert

sudo certbot --nginx

# this will lead you trough the setup process. If something fails, it is probably because you forgot to set the public dns or your firewall blocks port 80 to certbot

# Anyway, there are a lot of good tutorials online how to setup certbot.

# Certbot automatically changes your NGINX .conf file to listen on port 443

# if you run invoiceninja behind a proxy, here is a sample config.

# otherwise you can ignore this

server {


    listen       80;

    listen      [::]:80;

    # This is important or you will get 413 Server error from the proxy side

    client_max_body_size 20M;

    location / {

                    proxy_set_header        Host $host;

                    proxy_set_header        X-Real-IP $remote_addr;

                    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

                    proxy_set_header        X-Forwarded-Proto $scheme;

                    # Enter the IP or Internal DNS name of the server Invoice Ninja is installed on

                    proxy_pass    ;

                    proxy_redirect          off;



# You are done and hopefully everything is up and running!

# Next step would be setting up supervisor to get rid of the queque warning

# This is untested yet. You should better not use this :)

# optimize performance by using Laravel supervisor. This guide requires root access. If you don't have that, here is more documentation:


# install supervisor

sudo apt-get install supervisor

# configure supervisor

sudo nano /etc/supervisor/conf.d/laravel-worker.conf



command=php /usr/share/nginx/invoiceninja/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600










# save and exit

# start supervisor

sudo supervisorctl reread

sudo supervisorctl update

sudo supervisorctl start laravel-worker:*

# edit .env


INTERNAL_QUEUE_ENABLED=falseThis text will be hidden
1 Like


Thanks for sharing this!

Hi hillel, thank you for writing the code!

I would love to hear some feedback. This stuff is pretty new to me so I guess there is room for improvement :grinning: