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
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 versionshould return a 2.x version string.Create the docker-compose.yml file
Create
/opt/invoiceninja/docker-compose.ymlwith the following services. Thedbservice uses themysql:8.4image with a named volumedb_data. Theredisservice usesredis:7.4-alpine. Theappservice usesinvoiceninja/invoiceninja:5.13.45and depends ondbandredis; mount./data:/var/www/app/storageand./public:/var/www/app/public. Expose port9000internally only — nginx will handle the public interface. Declare all three services on an internalapp-netbridge network to isolate them from the rest of the host.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.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 (formatbase64:…) and set it asAPP_KEYin.env. Restart the app container:docker compose restart app. Without this step, all session tokens and database-encrypted data will be unreadable.Configure nginx as a reverse proxy
Create
/etc/nginx/sites-available/invoiceninja.conf. Theserverblock listens on port 443 with a Let's Encrypt certificate (see the tip block below). Therootdirective points to/opt/invoiceninja/public. Thelocation /block usestry_files $uri $uri/ /index.php?$query_string. Thelocation ~ \.php$block passes requests tofastcgi_pass 127.0.0.1:9000with standardSCRIPT_FILENAMEandPATH_INFOparameters. Addclient_max_body_size 100Mto allow large attachment uploads. Enable the configuration:ln -s /etc/nginx/sites-available/invoiceninja.conf /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx.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 openhttps://your-domain.comin 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.Enable the asynchronous job queue
Invoice Ninja relies on Laravel workers for email sending and background PDF generation. In the Compose file, add a
workerservice that uses the sameinvoiceninja/invoiceninja:5.13.45image, mounts the same volumes, and runs the commandphp 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.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 VPS | SaaS Invoice Ninja hosted | |
|---|---|---|
| Monthly cost | 99 DH/month (fixed VPS) | Free up to 20 clients, then $11–16/month |
| Client limit | Uncapped | 20 clients on the free plan |
| Data control | Data on your infrastructure | Data at Invoice Ninja Inc. |
| Updates | Manual (docker pull command) | Automatic |
| White-label | $30/year (optional) | Included in paid plans |
| Server maintenance | Your responsibility | Managed 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.