InvoiceShelf invoiceshelf/invoiceshelf:latest https://raw.githubusercontent.com/samuel3889/Unraid-Community-Applications/main/InvoiceShelf/InvoiceShelf.xml https://hub.docker.com/r/invoiceshelf/invoiceshelf bridge sh false https://github.com/InvoiceShelf/InvoiceShelf/issues https://github.com/InvoiceShelf/InvoiceShelf InvoiceShelf is a self-hosted, open source invoicing and estimates app built with Laravel and VueJS. ===================================================== READ THIS FIRST - 3 STEPS, THEN YOU'RE DONE ===================================================== STEP 1 - Create the database. This template runs the webapp only (pgsql variant). It does NOT deploy its own database and expects an external PostgreSQL 16 server or container to already be running. On that Postgres instance, create a database and a dedicated user that owns it, then fill in the DB_* fields below. Use a real password, not the Postgres superuser. Reference compose file: https://github.com/InvoiceShelf/docker/blob/master/docker-compose.pgsql.yml STEP 2 - Create the two appdata folders BEFORE clicking Apply. Paste this into the Unraid terminal (adjust paths if you changed the Uploads/Logs mappings below): mkdir -p /mnt/user/appdata/InvoiceShelf/uploads /mnt/user/appdata/InvoiceShelf/logs chown -R 82:82 /mnt/user/appdata/InvoiceShelf chmod -R 775 /mnt/user/appdata/InvoiceShelf Why: this image runs as the unprivileged user www-data (UID/GID 82:82 on Alpine). If Unraid creates these folders itself they come out root-owned and the setup wizard's permission check will fail. This is a one-time command - the ownership survives container recreation, image pulls and app updates, so you never run it again. STEP 3 - Run the setup wizard, and expect one red dot. On the wizard's Permissions screen, bootstrap/cache/ shows red. This is expected and harmless. That folder ships inside the image as mode 755, and the wizard compares the octal mode against 775 instead of testing whether the folder is actually writable. It IS writable - it is owned by www-data and Laravel caches config and routes into it successfully on every boot. Click Continue. storage/framework/ and storage/logs/ should both be green. If storage/logs/ is red, you skipped Step 2. ===================================================== STORAGE LAYOUT ===================================================== The full Laravel storage tree lives on a Docker-managed named volume (invoiceshelf_storage) which Docker auto-populates from the image on first start. Two folders are bind-mounted on top of it into /mnt/user/appdata so the Appdata Backup plugin can see them: Uploads = company logos and files added through the UI (Laravel's public disk). Logs = laravel.log, for troubleshooting. DO NOT bind-mount the whole /var/www/html/storage directory to an empty appdata folder. Unlike named volumes, bind mounts do not copy content out of the image, so doing that erases the shipped PDF templates and the container dies on boot with: The "/var/www/html/storage/app/templates/pdf" directory does not exist. The nested layout in this template exists specifically to avoid that. Consequence: the stock PDF templates stay inside the named volume and are not covered by Appdata Backup. That is fine for almost everyone, since they ship with the image and are restored on every pull. If you hand-edit them, back up the named volume separately. Your invoices, customers and payments live in your external Postgres database. Back that database up - this template cannot do it for you. ===================================================== REVERSE PROXY ===================================================== If running behind Nginx reverse proxy with a nonstandard port (eg. https://your-domain.com:50443), the custom configuration for location '/' must be: proxy_set_header X-Forwarded-Host $host:50443; proxy_set_header X-Forwarded-Port 50443; If login bounces you back to the login screen or throws a 419 / CSRF token mismatch, the host:port you typed in the browser is missing from SANCTUM_STATEFUL_DOMAINS. That variable must list every address you load the app from, including the port whenever it is not 80 or 443. false Productivity: http://[IP]:[PORT:8080]/ https://raw.githubusercontent.com/clairekardas/docker-icons/refs/heads/main/invoiceshelf.png -v invoiceshelf_storage:/var/www/html/storage/ -v invoiceshelf_modules:/var/www/html/Modules/ 2026-08-02 1) An already-running external PostgreSQL 16 server with a database + user created for InvoiceShelf - this template does not create it for you. 2) The two appdata folders created and chowned to 82:82 before first start (see Step 2 in the description above). 8090 /mnt/user/appdata/InvoiceShelf/uploads /mnt/user/appdata/InvoiceShelf/logs invoiceshelf invoiceshelf 5432 https://YOUR-DOMAIN.COM SERVER-IP:8090,YOUR-DOMAIN.COM * InvoiceShelf America/New_York production false pgsql file file 240 true false false 1