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

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.

  1. Open File Station in DSM.
  2. Inside your docker shared folder, create a folder called fireflyiii, 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

  1. Log into Portainer (http://<NAS-IP>:9000).
  2. In the left menu, click Stacks.
  3. Click + Add Stack.
  4. 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

  1. Scroll down in Portainer and click Deploy the Stack.
  2. Wait a few minutes for Portainer to download and start the containers.
  3. Once complete, you’ll see firefly , firefly-db ,firefly-db-backup ,importer and cron listed under Containers.

Step 5: Access Firefly III

  1. Open your browser and go to:http://<NAS-IP>:8080
  2. Complete the initial setup wizard by creating your admin account.
  3. Log in and start adding your bank accounts, expenses, and budgets!

Step 6: Next Steps (Optional Enhancements)


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.