Tutorial

Host Invoice Ninja on your VPS

Deployment10 min read8 steps

SaaS invoicing vendors charge per user or per transaction volume.

Contents· Why self-host your invoicing1/9
  1. 01Why self-host your invoicing
  2. 02What you get with Invoice Ninja 5.13.45
  3. 03Minimum requirements for Invoice Ninja 5.13.45
  4. 04Deploy Invoice Ninja on a VPS
  5. 05Connecting payment gateways
  6. 06Self-hosted Invoice Ninja vs hosted SaaS plan
  7. 07Backing up and updating Invoice Ninja
  8. 08Troubleshooting common errors
  9. 09Self-hosted Invoice Ninja: key takeaways

Why self-host your invoicing

SaaS invoicing vendors charge per user or per transaction volume. FreshBooks starts at $19/month for a single user; QuickBooks Online ranges from $35 to $90/month depending on the plan. As your team grows or your client count increases, the SaaS bill scales linearly with no corresponding increase in value. On an entry-level VPS at 99 DH/month, Invoice Ninja runs with no user or client limits, and the cost stays flat regardless of growth. Billing data remains in your infrastructure, subject to your own retention policy, without transiting through a third-party's servers. More than 100,000 businesses worldwide have already made this choice with Invoice Ninja's self-hosted deployment.

What you get with Invoice Ninja 5.13.45

  • Quotes, recurring invoices, credit notes, and purchase orders in a unified interface
  • Built-in payment gateways: Stripe, PayPal, SEPA, Mollie, GoCardless, and over twenty others
  • Self-hosted client portal with online invoice viewing and payment
  • Billable time tracking by project and task, directly linkable to invoice line items
  • Full REST API and webhooks to connect your ERP, CRM, or accounting pipeline
  • Generic OIDC authentication added in version 5.13.43 (18 September 2026) for enterprise SSO
  • Multi-currency and multi-language support with native server-side PDF rendering
  • Optional white-label module at $30/year to remove the 'Created with Invoice Ninja' footer

Minimum requirements for Invoice Ninja 5.13.45

Version 5.13.45, released on 9 October 2026, requires a specific minimum environment. Plan for at least 2 GB of RAM allocated to the entire stack: the Invoice Ninja PHP-FPM container consumes 300 to 500 MB under load, MySQL 8.4 LTS takes 400 to 700 MB depending on the buffer pool size, and Redis 7.4 stays light at 30–50 MB at rest. A VPS with 2 vCPUs and 4 GB of RAM provides comfortable headroom for parallel PDF generation. For storage, allocate at least 10 GB for Docker volumes: the database grows slowly, but invoice PDFs accumulate in storage/. MySQL 8.4 LTS is recommended by the official documentation for its compatibility guarantees through 2032; avoid MariaDB, which has known incompatibilities with certain Invoice Ninja v5 migrations. Redis 7.4 handles the asynchronous job queue (email sending, PDF generation). PHP 8.2 or 8.3 is bundled in the official invoiceninja/invoiceninja:5.13.45 image and does not need to be installed separately. Docker Engine 24+ and Docker Compose v2.x are required.

Deploy Invoice Ninja on a VPS

  1. Prepare the working directory

    SSH into your VPS and create a dedicated directory: mkdir -p /opt/invoiceninja/{data,logs,public} && cd /opt/invoiceninja. This directory will hold the Compose file, the environment file, and persistent volumes. Verify that Docker and Docker Compose v2 are installed: docker compose version should return a 2.x version string.

  2. Create the docker-compose.yml file

    Create /opt/invoiceninja/docker-compose.yml with the following services. The db service uses the mysql:8.4 image with a named volume db_data. The redis service uses redis:7.4-alpine. The app service uses invoiceninja/invoiceninja:5.13.45 and depends on db and redis; mount ./data:/var/www/app/storage and ./public:/var/www/app/public. Expose port 9000 internally only — nginx will handle the public interface. Declare all three services on an internal app-net bridge network to isolate them from the rest of the host.

  3. Configure environment variables

    Create /opt/invoiceninja/.env (never commit this file). Essential variables: APP_ENV=production, APP_DEBUG=false, APP_URL=https://your-domain.com, DB_HOST=db, DB_DATABASE=invoiceninja, DB_USERNAME=ninja, DB_PASSWORD=<strong_password>, MYSQL_ROOT_PASSWORD=<root_pwd>, REDIS_HOST=redis, QUEUE_CONNECTION=redis, MAIL_MAILER=smtp, MAIL_HOST=<your_smtp>, MAIL_PORT=587, MAIL_USERNAME=<account>, MAIL_PASSWORD=<pwd>, [email protected], MAIL_ENCRYPTION=tls. Use passwords of at least 32 random characters.

  4. Generate the APP_KEY application key

    Before the first start, generate the Laravel encryption key. First bring up the dependency containers: docker compose up -d db redis. Wait 20 seconds for MySQL to initialize its data directory, then start the application: docker compose up -d app. Generate the key: docker compose exec app php artisan key:generate. Copy the returned value (format base64:…) and set it as APP_KEY in .env. Restart the app container: docker compose restart app. Without this step, all session tokens and database-encrypted data will be unreadable.

  5. Configure nginx as a reverse proxy

    Create /etc/nginx/sites-available/invoiceninja.conf. The server block listens on port 443 with a Let's Encrypt certificate (see the tip block below). The root directive points to /opt/invoiceninja/public. The location / block uses try_files $uri $uri/ /index.php?$query_string. The location ~ \.php$ block passes requests to fastcgi_pass 127.0.0.1:9000 with standard SCRIPT_FILENAME and PATH_INFO parameters. Add client_max_body_size 100M to allow large attachment uploads. Enable the configuration: ln -s /etc/nginx/sites-available/invoiceninja.conf /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx.

  6. Run migrations and first login

    Once nginx is configured, run the database migrations: docker compose exec app php artisan migrate --force. The operation takes 30 to 60 seconds. Then open https://your-domain.com in a browser: the setup wizard prompts you to create an administrator account (email, password) and enter your company details (business name, IBAN if desired). After the wizard completes, Invoice Ninja is fully operational.

  7. Enable the asynchronous job queue

    Invoice Ninja relies on Laravel workers for email sending and background PDF generation. In the Compose file, add a worker service that uses the same invoiceninja/invoiceninja:5.13.45 image, mounts the same volumes, and runs the command php artisan queue:work --sleep=3 --tries=3. This service should not expose any port. Without an active worker, invoice emails are queued but never sent.

  8. Configure payment gateways

    In the backoffice (Settings > Payment Gateways), enable Stripe by pasting your public and secret keys from the Stripe dashboard. For PayPal, use the OAuth credentials from your PayPal REST application. For SEPA via GoCardless, enter your GoCardless Sandbox access token, then switch to the Production environment after testing. Each gateway can be enabled or disabled per currency and per client pricing plan.

To obtain a Let's Encrypt TLS certificate with Certbot, install the python3-certbot-nginx package and run certbot --nginx -d your-domain.com. Certbot automatically updates your nginx configuration to redirect HTTP to HTTPS and renews the certificate every 60 days via a systemd timer. Make sure port 80 is open in your firewall before running Certbot: ufw allow 80/tcp. Once the certificate is issued, you may close port 80 and enforce HTTPS by adding add_header Strict-Transport-Security "max-age=31536000" always; to your nginx server block.

Connecting payment gateways

Invoice Ninja 5.13.45 natively supports more than 25 gateways. Stripe is the most complete: it handles card payments, SEPA direct debit, ACH transfers, and, since version 5.13.43, Stripe PromptPay for Southeast Asian markets. To activate it, create a restricted API key in the Stripe dashboard with charges:write, customers:write, and payment_methods:read permissions, then enter it in Invoice Ninja. PayPal integrates via the REST API (not the legacy NVP API): create an application in the PayPal developer portal, retrieve the client_id and client_secret, and configure them in Invoice Ninja's PayPal gateway. For SEPA direct debits in Europe, GoCardless is the recommended route: the debit mandate is signed online by the client through the Invoice Ninja portal, without exchanging bank details by email. Mollie is a popular all-in-one alternative for European businesses, unifying card, bank transfer, iDEAL, and Bancontact under a single integration.

Self-hosted Invoice Ninja vs hosted SaaS plan

Scroll the table

Host Invoice Ninja yourself on VPSSaaS Invoice Ninja hosted
Monthly cost99 DH/month (fixed VPS)Free up to 20 clients, then $11–16/month
Client limitUncapped20 clients on the free plan
Data controlData on your infrastructureData at Invoice Ninja Inc.
UpdatesManual (docker pull command)Automatic
White-label$30/year (optional)Included in paid plans
Server maintenanceYour responsibilityManaged by the vendor

Backing up and updating Invoice Ninja

The backup strategy must cover two distinct elements: the MySQL database and the file storage volume. For MySQL, schedule a daily dump using docker compose exec db mysqldump -u ninja -p invoiceninja | gzip > /backups/invoiceninja-$(date +%Y%m%d).sql.gz via a cron job or systemd timer. For files (generated PDFs, logos, attachments), back up the ./data directory with rsync to an S3-compatible object store. Retain at least 30 rolling days and test restoration quarterly.

To update to a future version, the procedure is: update the image tag in your Compose file (for example from 5.13.45 to the next version), pull the new image with docker compose pull app, then run docker compose up -d app worker followed by docker compose exec app php artisan migrate --force. The migration runs automatically on startup in some configurations, but it is safer to trigger it explicitly. Review the GitHub changelog before any major update (for example a 5.x to 6.x jump): additional migration steps may be required.

Troubleshooting common errors

Four issues come up frequently during initial deployment.

Error SQLSTATE[HY000] [2002] Connection refused: MySQL is not yet ready when the app container tries to connect. Add a healthcheck directive to the db service and a service_healthy condition in the app's depends_on. Alternatively, wait 30 seconds after docker compose up -d db before starting the app.

Emails are not sent: verify that the worker service is running with docker compose ps worker. If the status shows Exited, inspect the logs: docker compose logs worker. The most common cause is an incorrect MAIL_HOST or an SMTP port blocked by the hosting provider's firewall.

Blank page or 500 error after migration: the storage/ directory has incorrect permissions. From the host, run docker compose exec app chown -R www-data:www-data /var/www/app/storage && docker compose exec app php artisan config:clear && docker compose exec app php artisan cache:clear.

PDF generation produces empty files: Invoice Ninja uses Snappy (wkhtmltopdf) or an alternative depending on configuration. Check that the PDF_GENERATOR variable is set (possible values: snappy, browsershot, phantom) and that the corresponding binary is present in the image. The official invoiceninja/invoiceninja:5.13.45 image includes wkhtmltopdf; if you built a custom image, ensure you included it.

Self-hosted Invoice Ninja: key takeaways

Invoice Ninja 5.13.45, published on 9 October 2026, is a mature and actively maintained invoicing solution. The Elastic License 2.0 permits self-hosting for any internal use without restriction: you owe nothing as long as you do not resell access as a service. The Docker stack (official image + MySQL 8.4 LTS + Redis 7.4) is reproducible in under an hour on any VPS with 2 GB of RAM. The white-label module remains optional at $30/year: for the vast majority of freelancers and SMBs, the 'Created with Invoice Ninja' footer on PDFs is not an obstacle. If your client volume exceeds 20 or your billing data must remain in your infrastructure for legal or compliance reasons, self-hosted deployment is both economically and technically justified. Our team is available at [email protected] to help you right-size your VPS and configure backups.

Deploy your infrastructure today

VPS Cloud. Preconfigured environments available.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab