Introduction
If you're a freelancer or you run a small shop, there's a decent chance your invoicing setup right now is a Google Docs template, a spreadsheet, or a SaaS tool that charges you a few hundred dollars a year to send maybe a dozen invoices a month. None of those are great. The spreadsheet doesn't track payments. The SaaS tool owns your client data and your invoice history, and if you ever cancel, good luck exporting it cleanly.
Invoice Ninja is a free, open-source alternative that does quotes, recurring invoices, expense tracking, and online payments, and you can run the whole thing yourself. On a Proxmox VE host, the easiest way to do that is inside an LXC container using the community-scripts installer — a script that builds the container, installs the app, and gets you to a working login screen in a few minutes instead of an afternoon of manual server setup.
This guide walks through the whole process on Proxmox VE 9.2: creating the container, running the initial setup wizard, wiring up outgoing email so invoice reminders actually get delivered, and fixing the two or three things that trip people up on their first run.
What You Will Learn
- What Invoice Ninja actually does and who it's built for
- Why running it yourself beats a monthly invoicing subscription for most freelancers and small teams
- What you need before you start (and what you don't)
- How to install Invoice Ninja in an LXC container using the community-scripts helper script
- How to finish the first-run setup wizard and create your admin account
- How to configure SMTP so invoices and payment reminders actually send
- Common errors during setup and how to fix each one
- Backup and security habits worth adopting from day one
What Is Invoice Ninja?
Invoice Ninja is an open-source billing platform built with PHP and Laravel. It handles the parts of running a small business that usually eat up a Sunday afternoon: creating quotes, converting them to invoices, setting up recurring billing for retainer clients, tracking expenses, and accepting online payments through Stripe, PayPal, or several other gateways. Clients get a portal where they can view and pay invoices without emailing you back and forth about which PDF is the current one.
There's a hosted version (Invoice Ninja Cloud) if you'd rather not manage a server at all, but the free tier caps you at a small number of clients and invoices per month, and the self-hosted edition removes those limits entirely because you're the one running the infrastructure.
We're installing it inside an LXC container, which is worth explaining if you haven't used one before. An LXC container is a lightweight, isolated Linux environment that shares the host's kernel instead of emulating its own virtual hardware the way a full VM does. That makes it start faster and use noticeably less RAM and disk than a comparable VM, which is exactly what you want for something like Invoice Ninja that mostly sits around waiting for HTTP requests.
The community-scripts project maintains ready-made installer scripts for hundreds of self-hosted apps, Invoice Ninja included. Instead of manually installing PHP, MySQL, Redis, and a web server and wiring them all together by hand, you run one command in the Proxmox shell and the script does it for you, using sane defaults you can still change later.
Why Would You Use It?
The obvious reason is cost. A lot of small invoicing SaaS plans run $10 to $25 a month once you're past their free tier, and that's money you're paying indefinitely for something that, once installed, costs you almost nothing to keep running. An Invoice Ninja container idles at a couple hundred megabytes of RAM and barely touches the CPU between requests.
The less obvious reason is control. Your client list, your invoice history, and your payment records live on hardware you own, not on a third party's servers where a pricing change, an acquisition, or a policy update could suddenly affect how you run your business. You can also customize the invoice templates, the client portal branding, and the email wording without waiting for a vendor to add that as a "premium" feature.
Honestly, if you're already comfortable running one or two LXC containers on Proxmox for other homelab projects, adding Invoice Ninja is a small lift. If you've never touched a self-hosted app before, this is a reasonable first one to try — it has a real, tangible use in your day-to-day work instead of being another dashboard you'll forget to check.
Prerequisites
Before you start, make sure you have the following:
- A working Proxmox VE 8.x or 9.x installation with root access to the web interface or shell
- At least 8 GB of free storage on the target storage location for the container's disk
- A basic idea of your network setup — whether the container will get an IP from DHCP or you plan to assign one statically
- Outgoing SMTP credentials (from something like Gmail, a transactional email provider, or your own mail server) if you want Invoice Ninja to actually send invoice emails and payment reminders
- A domain name pointed at your network, if you eventually want to expose the client portal outside your LAN — not required just to get it running locally
You don't need any PHP or Laravel experience. The installer handles the application stack; you're mostly interacting with a web setup wizard after that.
Step-by-Step Tutorial
Log in to the Proxmox VE web interface, select any node in the left-hand tree, and open a Shell session for that node. Everything below runs from there, as root.
Step 1: Run the Invoice Ninja installer
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/invoiceninja.sh)"
This downloads and runs the community-scripts installer for Invoice Ninja. The script walks you through a text-based menu — choose Default Settings unless you specifically need to change the container's storage location, VMID, or resource allocation, in which case pick Advanced instead.
Step 2: Let it build the container
With default settings, the script creates an unprivileged Debian 13 LXC container with 2 CPU cores, 2048 MB of RAM, and an 8 GB disk, then installs PHP, MariaDB, Redis, and Invoice Ninja itself inside it. This takes somewhere between three and eight minutes depending on your internet connection, since it's pulling packages and the application source fresh.
When it finishes, the script prints the container's IP address and tells you the web setup is available on port 8080. It also writes a credentials file inside the container at ~/invoiceninja.creds containing the database name, username, and password the installer generated for you. You'll need those in the next step.
Step 3: Open the setup wizard
From a browser on the same network, go to http://<container-ip>:8080/setup. Invoice Ninja's setup wizard checks your PHP environment, then asks for database connection details.
To read the generated credentials, get a shell inside the container from the Proxmox host:
pct enter <container-id>
cat ~/invoiceninja.creds
Copy the database name, username, and password into the setup form exactly as shown. A typo here is the single most common reason the wizard hangs on "testing connection" and never moves forward.
Step 4: Create your admin account and company
Once the database check passes, the wizard asks for your admin email and password, then your company name, address, and default currency. Fill these in — you can change all of it later from Settings, so don't overthink it now.
Step 5: Configure outgoing email
By default, Invoice Ninja isn't configured to send email, which means invoices, quotes, and payment reminders won't actually reach anyone until you set up SMTP. Inside the container, edit the environment file:
nano /opt/invoiceninja/.env
Set MAIL_MAILER to smtp, then fill in MAIL_HOST, MAIL_PORT, MAIL_USERNAME, MAIL_PASSWORD, and MAIL_ENCRYPTION with your provider's details. Save the file, then restart the app's queue worker and web service so the new settings take effect — the exact service names are listed in the script's output when the install finished; restart each one with systemctl restart <service-name>.
Step 6: Send a test invoice
Create a test client with your own email address, generate a quick invoice, and email it to yourself. If it lands in your inbox, email is working. If it doesn't, jump to the Troubleshooting section below before you go any further.
Commands Explained
A quick rundown of what each command you just ran actually does:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/invoiceninja.sh)"— downloads the installer script from the community-scripts GitHub repository and immediately runs it. The-fsSLflags oncurlmake it fail silently on server errors, follow redirects, and suppress the progress bar, which is standard for piping a script straight into bash.pct enter <container-id>— opens an interactive shell directly inside the specified LXC container, without needing to SSH into it separately. It's the fastest way to poke around a container from the Proxmox host.cat ~/invoiceninja.creds— prints the contents of the credentials file the installer generated, so you can read the auto-generated database password without guessing it.nano /opt/invoiceninja/.env— opens Invoice Ninja's environment configuration file in the nano text editor. This file holds database, mail, and application settings as plain key-value pairs.systemctl restart <service-name>— restarts a background service so it picks up changes you just made to a config file. Most Laravel apps, Invoice Ninja included, cache configuration in memory and won't notice a changed .env file until the process restarts.
Common Errors
A few issues show up often enough with fresh installs that they're worth calling out by name.
Blank white screen or HTTP 500 after finishing setup
This is usually a file permissions problem on the storage or bootstrap/cache directories that Laravel writes to. It tends to show up when someone manually copies files into the container or changes ownership while poking around. Check the app's log file at storage/logs/laravel.log inside the Invoice Ninja directory for the actual error before doing anything else.
"cURL error 60: SSL certificate problem"
This shows up when Invoice Ninja tries to reach an external API (currency exchange rates, for example) and the container's CA certificate bundle is out of date. Running apt update && apt install --reinstall ca-certificates inside the container clears it up in almost every case.
Setup wizard stuck on "Testing database connection"
Nine times out of ten this is a copy-paste mistake with the credentials from ~/invoiceninja.creds. Double-check for trailing spaces or a missed character, especially in the password field.
Emails never arrive, no error shown
If you skipped the SMTP configuration step, Invoice Ninja falls back to a null mail driver that silently discards anything it tries to send. There's no error because, as far as the app is concerned, nothing went wrong — it just has nowhere to actually deliver the message.
Troubleshooting
Start with the basics before digging into application logs. From the Proxmox host, confirm the container is actually running:
pct status <container-id>
If it's stopped, start it with pct start <container-id> and try loading the page again. If it's running but the page won't load, check that the web service inside the container is up and listening, and confirm you're using the right IP — it can change if the container is using DHCP and gets a new lease after a reboot. Setting a static IP for anything you rely on daily saves this headache entirely.
If the page loads but something specific is broken (invoices not generating a PDF, for instance), the Laravel log inside the app almost always names the exact problem. Tail it while reproducing the issue:
tail -f /opt/invoiceninja/storage/logs/laravel.log
Disk space is worth a quick check too. Laravel apps generate a fair amount of cache and log data over time, and an 8 GB container disk can fill up faster than you'd expect if logging is left at a verbose level. Run df -h inside the container to see where you stand.
Best Practices
A few habits worth adopting right after setup, not six months later when something breaks:
- Put Invoice Ninja behind a reverse proxy with a real TLS certificate before you send it a link to click, especially if the client portal will ever be reachable outside your home network. An Nginx Proxy Manager container with a free Let's Encrypt certificate covers this in about ten minutes.
- Schedule regular Proxmox backups of the container using vzdump, either through the GUI's backup jobs or a cron schedule. Your invoice history and client payment records are exactly the kind of data you don't want to lose to a disk failure.
- Change the default admin password immediately if you used anything simple during setup, and turn on two-factor authentication in Invoice Ninja's account settings for the admin login.
- Keep the app updated. The community-scripts installer includes an update function — re-running the same install command against an existing container offers to update it in place rather than creating a new one.
- Don't expose port 8080 directly to the internet through port forwarding. Route external access through a reverse proxy instead, so you're not relying on the app's own web server to handle internet-facing traffic securely.
Frequently Asked Questions
Is Invoice Ninja really free?
The self-hosted version is free and open source under its own license, with no cap on clients or invoices. The company also sells a hosted cloud version with paid tiers, but that's a separate product from what you're installing here.
Can I accept credit card payments through it?
Yes. Invoice Ninja supports Stripe, PayPal, and several other payment gateways. You'll need an account with whichever provider you choose and to add their API keys in Invoice Ninja's settings.
Do I need a domain name to use this?
No, not for local use on your home or office network. You'll want one if you plan to let clients access the portal from outside your network, since it makes setting up HTTPS through a reverse proxy much easier.
How much RAM does this actually need once it's running?
Idle, expect it to sit well under the 2 GB the container is allocated. Under load — generating PDFs for a batch of invoices, for example — it'll spike higher briefly. For a solo freelancer or a small team, the defaults are plenty.
Can I migrate from another invoicing tool?
Invoice Ninja supports CSV imports for clients and some other data types. A full migration from a different platform usually means some manual cleanup, but you won't have to re-enter every client from scratch.
Conclusion
Invoice Ninja isn't the flashiest thing you'll run in a homelab, but it's one of the few self-hosted apps that pays for its own setup time almost immediately — every invoice you send afterward is one you didn't pay a SaaS fee for. The LXC container approach keeps the resource footprint small enough that it's a rounding error next to whatever else your Proxmox host is running, and the community-scripts installer means you're not hand-rolling a PHP and MySQL stack to get there.
Once it's running, spend a few minutes on the SMTP setup and a reverse proxy before you send your first real invoice to a client. Both take longer to explain than to actually do, and skipping them is the difference between a smooth first impression and a client emailing you asking why the payment link looks broken.