Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Docker Deploy Invoice Ninja

The easiest way to deploy Invoice Ninja is through docker which I will show you below. However if you wish to manually deploy it, this guide might be useful.

Requirements

I managed to deploy on a VM with only 512 MB of RAM. However, because we will be using a database container, and MySQL has a habbit of growing in memory, I would recommend deploying on a 1GB instance.

Steps

Install docker.

Install docker-compose.

Grab the example.

#!/bin/bash
mkdir -p $HOME/invoice-ninja/src
git clone https://github.com/invoiceninja/dockerfiles.git $HOME/invoice-ninja/src/.
mv $HOME/invoice-ninja/src/docker-compose/.env.example $HOME/invoice-ninja/env
mv $HOME/invoice-ninja/src/docker-compose/nginx.conf $HOME/invoice-ninja/nginx.conf
mv $HOME/invoice-ninja/src/docker-compose/docker-compose.yml $HOME/invoice-ninja/.
rm -rf $HOME/invoice-ninja/src/

You should now have a folder called invoice-ninja in your home directory which contains the following files:

  • docker-compose.yml
  • env
  • nginx.conf

Edit Environment Variables

The env file will look like below (unless it has changed since I wrote this tutorial).

MYSQL_DATABASE=ninja
MYSQL_ROOT_PASSWORD=pwd

APP_DEBUG=0
APP_URL=http://localhost:8000
APP_KEY=SomeRandomStringSomeRandomString
APP_CIPHER=AES-256-CBC
DB_USERNAME=root
DB_PASSWORD=pwd
DB_HOST=db
DB_DATABASE=ninja
MAIL_HOST=mail.service.host
MAIL_USERNAME=username
MAIL_PASSWORD=password
MAIL_DRIVER=smtp
MAIL_FROM_NAME="My name"
MAIL_FROM_ADDRESS=user@mail.com

Run the following script to set random passwords in the env file:

#!/bin/bash
DB_PASSWORD=`openssl rand -base64 12`
APP_KEY=`openssl rand -base64 24`
FILEPATH="$HOME/invoice-ninja/env"

# Update the app key
SEARCH="APP_KEY=SomeRandomStringSomeRandomString"
REPLACE="APP_KEY=$APP_KEY"
sed -i "s;$SEARCH;$REPLACE;" $FILEPATH

# Update mysql root password
SEARCH="MYSQL_ROOT_PASSWORD=pwd"
REPLACE="MYSQL_ROOT_PASSWORD=$DB_PASSWORD"
sed -i "s;$SEARCH;$REPLACE;" $FILEPATH

# Update app db password setting to be the same
SEARCH="DB_PASSWORD=pwd"
REPLACE="DB_PASSWORD=$DB_PASSWORD"
sed -i "s;$SEARCH;$REPLACE;" $FILEPATH

It's very important to change the the following:

It is important to ensure the following is the case:

  • APP_DEBUG is 0
  • The APP_KEY has been updated to a random string. E.g should not be: SomeRandomStringSomeRandomString
  • The DB_PASSWORD and MYSQL_ROOT_PASSWORD have been updated to a random string and not set to pwd.

By the end of it, you should have something like:

MYSQL_DATABASE=ninja
MYSQL_ROOT_PASSWORD=Htw0GOdwCF6NNt3E

APP_DEBUG=0
APP_URL=http://invoices.mydomain.com
APP_KEY=rfQH/9dZTjO4v4gARlnTz4oBrgin28qD
APP_CIPHER=AES-256-CBC
DB_USERNAME=root
DB_PASSWORD=Htw0GOdwCF6NNt3E
DB_HOST=db
DB_DATABASE=ninja
MAIL_HOST=smtp.gmail.com
MAIL_USERNAME=my.email@gmail.com
MAIL_PASSWORD=myAppSpecificPassword
MAIL_DRIVER=smtp
MAIL_FROM_NAME="Programster Blogs"
MAIL_FROM_ADDRESS=my.email@gmail.com

Edit docker-compose.yml

Now go into your docker-compose.yml file and change:

  • the database password to match whatever you set in the env file in the previous step.
  • .env to env.
  • the port line from 8000:80 to 80:80.
    • This will allow the site to be available without needing to enter a port into the URL bar.

You should end up with a docker-compose file that looks like the following:

version: '3.6'

volumes:
  db:
  storage:
  logo:
  public:

# uncomment if you want to use external network (example network: "web")
#networks:
#  web:
#    external: true

services:
  db:
    image: mysql:5
    env_file: env
    restart: always
    volumes:
      - db:/var/lib/mysql
    networks:
      - default

  app:
    image: invoiceninja/invoiceninja
    env_file: env
    restart: always
    depends_on:
      - db
    volumes:
      -  storage:/var/www/app/storage
      -  logo:/var/www/app/public/logo
      -  public:/var/www/app/public
    networks:
      - default

  web:
    image: nginx:1
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      -  storage:/var/www/app/storage
      -  logo:/var/www/app/public/logo
      -  public:/var/www/app/public
    expose: # Expose ports without publishing them to the host machine - they’ll only be accessible to linked services.
      - "80"
    depends_on:
      - app
    ports: # Delete if you want to use reverse proxy
      - 80:80
    networks:
#      - web        # uncomment if you want to use  external network (reverse proxy for example)
      - default

  cron:
    image: invoiceninja/invoiceninja
    env_file: env
    volumes:
      -  storage:/var/www/app/storage
      -  logo:/var/www/app/public/logo
      -  public:/var/www/app/public
    entrypoint: |
      bash -c 'bash -s <<EOF
      trap "break;exit" SIGHUP SIGINT SIGTERM
      sleep 300s
      while /bin/true; do
        ./artisan ninja:send-invoices
        ./artisan ninja:send-reminders
        sleep 1d
      done
      EOF'
    networks:
      - default

I made the env file a non-hidden file so that it is not accidentally forgotten about as it's fairly important as it contains your passwords etc.

Deploy!

Now you can deploy with the following command:

docker-compose up

Web UI Setup

Now navigate to your server's IP or hostname and you should be presented with a form to fill in like below:

Most of the details should already be filled in for you (from the environment file earlier). You just need to enter your name details and submit. It would be a good idea to make use of button to send a test email to check your SMTP details are correct. If you do not see this screen please refer to the debugging section below.

State

All state is held in the docker volumes called: * db * storage * logo * public

You will have seen these at the top of the docker-compose file section like so:

volumes:
  db:
  storage:
  logo:
  public:

You can use the docker volume commands to interact with them. E.g. list them with:

docker volume ls

The actual data will default to being located under:

/var/lib/docker/volumes

Debugging

If you do not see the Web UI Setup form then something went wrong. It would be a good idea to re-deploy with APP_DEBUG set to 1 and then you will get an error message in the web interface. It is also good to read the text that appears in the CLI after you ran docker-compose up.

Common issues are:

  • Database password not matching in the compose file and the environment file.
  • The APP_KEY string not being long enough.

Conclusion

You should now have your own open source invoice solution. In future I will show how to get SSL enabled.

Last updated: 12th May 2020
First published: 16th August 2018