Running Containers on a Synology DS1821+: How to Install Firefly III with Portainer

Introduction
In our previous guide, we set up Portainer on the Synology DS1821+ to simplify container management. Now it’s time to put it to use by installing our first real application: Firefly III.
Firefly III is a powerful, self-hosted personal finance manager that helps you track expenses, budgets, and investments—all while keeping your sensitive data on your own hardware.
In this tutorial, we’ll walk through installing Firefly III on Synology using Portainer and Docker Compose.
What You’ll Need
- A Synology DS1821+ (or similar Synology NAS)
- Docker/Container Manager and Portainer already set up
- At least 2GB of free RAM
- A dedicated shared folder for app data (e.g.,
docker/firefly
)
Step 1: Prepare Your Environment
Before deploying Firefly III, you’ll want to set up persistent storage for both the application and its database.
- Open File Station in DSM.
- Inside your
docker
shared folder, create a folder calledfireflyiii
, with four subfolders:importer
db
db-backup
upload
These will store application and database data across container restarts.
Step 2: Create a New Stack in Portainer
- Log into Portainer (
http://<NAS-IP>:9000
). - In the left menu, click Stacks.
- Click + Add Stack.
- Name the stack:
firefly
.
Step 3: Define the Docker Compose File
Paste the following Docker Compose configuration into the editor:
version: "3.7"
networks:
internal:
external: false
services:
firefly:
container_name: firefly
image: fireflyiii/core:latest
ports:
- 8080:8080
volumes:
- /volume1/fireflyiii/upload:/var/www/html/storage/upload
restart: unless-stopped
env_file:
- stack.env
depends_on:
- firefly-db
networks:
- internal
firefly-db:
container_name: firefly-db
image: postgres:14
volumes:
- /volume1/fireflyiii/db:/var/lib/postgresql/data
restart: unless-stopped
environment:
POSTGRES_DB: ${DB_DATABASE}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
networks:
- internal
firefly-db-backup:
container_name: firefly-db-backup
image: postgres:14
volumes:
- /volume1/fireflyiii/db-backup:/dump
- /etc/localtime:/etc/localtime:ro
environment:
PGHOST: firefly-db
PGDATABASE: ${DB_DATABASE}
PGUSER: ${DB_USERNAME}
PGPASSWORD: ${DB_PASSWORD}
BACKUP_NUM_KEEP: 10
BACKUP_FREQUENCY: 7d
entrypoint: |
bash -c 'bash -s <<EOF
trap "break;exit" SIGHUP SIGINT SIGTERM
sleep 2m
while /bin/true; do
pg_dump -Fc > /dump/dump_\`date +%d-%m-%Y"_"%H_%M_%S\`.psql
(ls -t /dump/dump*.psql|head -n $$BACKUP_NUM_KEEP;ls /dump/dump*.psql)|sort|uniq -u|xargs rm -- {}
sleep $$BACKUP_FREQUENCY
done
EOF'
networks:
- internal
firefly-redis:
container_name: firefly-redis
image: redis:7
networks:
- internal
importer:
image: fireflyiii/data-importer:latest
hostname: importer
restart: always
container_name: firefly-importer
networks:
- internal
env_file:
- stack.env
volumes:
- /volume1/fireflyiii/importer:/import
ports:
- '8081:8080'
depends_on:
- firefly
cron:
#
image: alpine
container_name: firefly-cron
restart: always
command: sh -c "
apk add tzdata
&& rm -f /etc/localtime
&& ln -s /usr/share/zoneinfo/${TZ} /etc/localtime
&& echo -e \"0 3 * * * wget -qO- http://firefly:8080/api/v1/cron/YOUR-CRON-TOKEN\" > /tmp/crontab_tmp
&& echo -e \"40 6,18 * * * wget -qO - --post-data '' --header 'Accept":" application/json' 'http://importer:8080/autoimport?directory=/import&secret=YOUR-SECRET'\" >> /tmp/crontab_tmp
&& crontab /tmp/crontab_tmp
&& crond -f -L /dev/stdout
&& rm /tmp/crontab_tmp"
networks:
- internal
Enironment file
APP_ENV=production
APP_DEBUG=false
SITE_OWNER=OWNER@DOMAIN.com
APP_KEY=YOUR-APP-KEY
DEFAULT_LANGUAGE=en_US
DEFAULT_LOCALE=equal
TZ=Europe/London
TRUSTED_PROXIES=**
LOG_CHANNEL=stack
APP_LOG_LEVEL=notice
AUDIT_LOG_LEVEL=emergency
AUDIT_LOG_CHANNEL=
PAPERTRAIL_HOST=
PAPERTRAIL_PORT=
DB_CONNECTION=pgsql
DB_HOST=firefly-db
DB_PORT=5432
DB_DATABASE=firefly
DB_USERNAME=firefly
DB_PASSWORD=DB-PASS
DB_SOCKET=
MYSQL_USE_SSL=false
MYSQL_SSL_VERIFY_SERVER_CERT=true
MYSQL_SSL_CAPATH=/etc/ssl/certs/
MYSQL_SSL_CA=
MYSQL_SSL_CERT=
MYSQL_SSL_KEY=
MYSQL_SSL_CIPHER=
PGSQL_SSL_MODE=prefer
PGSQL_SSL_ROOT_CERT=null
PGSQL_SSL_CERT=null
PGSQL_SSL_KEY=null
PGSQL_SSL_CRL_FILE=null
PGSQL_SCHEMA=public
CACHE_DRIVER=redis
SESSION_DRIVER=file
REDIS_SCHEME=tcp
REDIS_PATH=
REDIS_HOST=firefly-redis
REDIS_PORT=6379
REDIS_USERNAME=
REDIS_PASSWORD=
REDIS_DB="0"
REDIS_CACHE_DB="1"
COOKIE_PATH="/"
COOKIE_DOMAIN=
COOKIE_SECURE=false
COOKIE_SAMESITE=lax
MAIL_MAILER=smtp
MAIL_HOST=mail.DOMAIN.com
MAIL_PORT=25
MAIL_FROM=firefly@DOMAIN.com
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_SENDMAIL_COMMAND=
MAIL_ALLOW_SELF_SIGNED=false
MAIL_VERIFY_PEER=true
MAIL_VERIFY_PEER_NAME=true
MAILGUN_DOMAIN=
MAILGUN_SECRET=
MAILGUN_ENDPOINT=api.mailgun.net
MANDRILL_SECRET=
SPARKPOST_SECRET=
MAILERSEND_API_KEY=
SEND_ERROR_MESSAGE=true
SEND_REPORT_JOURNALS=true
ENABLE_EXTERNAL_MAP=false
ENABLE_EXCHANGE_RATES=false
ENABLE_EXTERNAL_RATES=false
MAP_DEFAULT_LAT=51.983333
MAP_DEFAULT_LONG=5.916667
MAP_DEFAULT_ZOOM=6
VALID_URL_PROTOCOLS=
AUTHENTICATION_GUARD=web
AUTHENTICATION_GUARD_HEADER=REMOTE_USER
AUTHENTICATION_GUARD_EMAIL=
PASSPORT_PRIVATE_KEY=
PASSPORT_PUBLIC_KEY=
CUSTOM_LOGOUT_URL=
DISABLE_FRAME_HEADER=false
DISABLE_CSP_HEADER=false
TRACKER_SITE_ID=
TRACKER_URL=
ALLOW_WEBHOOKS=true
STATIC_CRON_TOKEN=YOUR-CRON-TOKEN
DKR_BUILD_LOCALE=false
DKR_CHECK_SQLITE=true
APP_NAME=FireflyIII
BROADCAST_DRIVER=log
QUEUE_DRIVER=sync
CACHE_PREFIX=firefly
PUSHER_KEY=
IPINFO_TOKEN=
PUSHER_SECRET=
PUSHER_ID=
DEMO_USERNAME=
DEMO_PASSWORD=
USE_RUNNING_BALANCE=false
FIREFLY_III_LAYOUT=v1
QUERY_PARSER_IMPLEMENTATION=legacy
APP_URL=http://localhost
FIREFLY_III_URL=http://firefly:8080
FIREFLY_III_ACCESS_TOKEN=YOUR-ACCESS-KEY
VERIFY_TLS_SECURITY=false
AUTO_IMPORT_SECRET=YOUR-SECRET
CAN_POST_FILES=true
CAN_POST_AUTOIMPORT=true
IMPORT_DIR_ALLOWLIST=/import
NORDIGEN_ID=YOUR-ID
NORDIGEN_KEY=YOUR-KEY
Important Notes:
- Replace
APP_KEY
with a secure 32-character random string. You can generate one with:openssl rand -base64
32 - Replace any variable starting with "YOUR-"
- Adjust the
/volume1/docker/...
paths if your shared folder is located elsewhere.
Step 4: Deploy the Stack
- Scroll down in Portainer and click Deploy the Stack.
- Wait a few minutes for Portainer to download and start the containers.
- Once complete, you’ll see
firefly
,firefly-db
,firefly-db-backup
,importer
andcron
listed under Containers.
Step 5: Access Firefly III
- Open your browser and go to:
http://<NAS-IP>:8080
- Complete the initial setup wizard by creating your admin account.
- Log in and start adding your bank accounts, expenses, and budgets!
Step 6: Next Steps (Optional Enhancements)
- HTTPS Access: Use Synology’s built-in Reverse Proxy (Control Panel → Application Portal) to map
https://finance.mydomain.com
to Firefly’s container. - Setup Paperless-ngx for managing actual receipts.
- Configuring the importer to automatically import your transactions.
- Link Paperless-ngx to Firefly with Node-red.
Conclusion
With Firefly III running on your Synology DS1821+ via Portainer, you now have a secure, self-hosted finance tracker that rivals commercial apps—without giving up control of your data.
This is just the beginning. With Portainer managing your stacks, adding more self-hosted applications (like Paperless-NGX, Nextcloud, Vaultwarden, or Home Assistant) is only a few clicks away.
About the author
Tim Wilkes is a UK-based security architect with over 15 years of experience in electronics, Linux, and Unix systems administration. Since 2021, he's been designing secure systems for a telecom company while indulging his passions for programming, automation, and 3D printing. Tim shares his projects, tinkering adventures, and tech insights here - partly as a personal log, and partly in the hopes that others will find them useful.
Want to connect or follow along?
LinkedIn: [phpsytems]
Twitter / X: [@timmehwimmy]
Mastodon: [@timmehwimmy@infosec.exchange]
If you've found a post helpful, consider supporting the blog - it's a part-time passion that your support helps keep alive.
⚠️ Disclaimer
This post may contain affiliate links. If you choose to purchase through them, I may earn a small commission at no extra cost to you. I only recommend items and services I’ve personally read or used and found valuable.
As an Amazon Associate I earn from qualifying purchases.