Wealthfolio logo Wealthfolio
Download
Docs
Self-Hosting Guide

Self-Hosting Guide

Complete guide to deploying and configuring Wealthfolio on your own infrastructure using Docker


Self-Hosting Wealthfolio

Deploy Wealthfolio on your own infrastructure with full control over your data and configuration. This guide covers Docker deployment and all available configuration options.

Quick Start with Docker

Prerequisites

  • Docker installed on your system (Install Docker)
  • Basic familiarity with command line
  • A secure location to store your configuration

Step 1: Pull the Docker Image

docker pull afadil/wealthfolio:latest

Step 2: Configure the Container

You can configure the container using either:

  1. Environment file (recommended for production) - using --env-file flag
  2. Inline environment variables - using -e flag (simpler for testing)

Create a Docker-specific environment file:

cat > .env.docker << 'EOF'
WF_LISTEN_ADDR=0.0.0.0:8088
WF_DB_PATH=/data/wealthfolio.db
WF_CORS_ALLOW_ORIGINS=*
WF_REQUEST_TIMEOUT_MS=30000
WF_STATIC_DIR=dist
# Security (Required)
WF_SECRET_KEY=replace_with_generated_secret
# Authentication (Required for web access)
WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...'
EOF

Generate a secret key:

openssl rand -base64 32

Important for Docker: The server must bind to 0.0.0.0 (all interfaces) inside the container to be accessible from your host machine. Binding to 127.0.0.1 will make the app only accessible from within the container.

Option 2: Use Inline Environment Variables

For simpler testing, you can pass environment variables directly with the -e flag. See the running examples below.

Step 3: Run the Container

docker run --rm -d \
  --name wealthfolio \
  --env-file .env.docker \
  -p 8088:8088 \
  -v "$(pwd)/wealthfolio-data:/data" \
  afadil/wealthfolio:latest

Basic Usage (Inline Environment Variables)

docker run --rm -d \
  --name wealthfolio \
  -e WF_LISTEN_ADDR=0.0.0.0:8088 \
  -e WF_DB_PATH=/data/wealthfolio.db \
  -e WF_SECRET_KEY=$(openssl rand -base64 32) \
  -e WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...' \
  -p 8088:8088 \
  -v "$(pwd)/wealthfolio-data:/data" \
  afadil/wealthfolio:latest

Development Mode (with CORS for Local Dev Server)

docker run --rm -it \
  --name wealthfolio \
  -e WF_LISTEN_ADDR=0.0.0.0:8088 \
  -e WF_DB_PATH=/data/wealthfolio.db \
  -e WF_CORS_ALLOW_ORIGINS=http://localhost:1420 \
  -e WF_SECRET_KEY=$(openssl rand -base64 32) \
  -e WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...' \
  -p 8088:8088 \
  -v "$(pwd)/wealthfolio-data:/data" \
  afadil/wealthfolio:latest
docker run --rm -d \
  --name wealthfolio \
  -e WF_LISTEN_ADDR=0.0.0.0:8088 \
  -e WF_DB_PATH=/data/wealthfolio.db \
  -e WF_SECRET_KEY=$(openssl rand -base64 32) \
  -e WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...' \
  -p 8088:8088 \
  -v "$(pwd)/wealthfolio-data:/data" \
  afadil/wealthfolio:latest

Step 4: Access Your Instance

Open your browser and navigate to http://localhost:8088

Docker Volumes and Ports

Volumes

The container uses /data for persistent storage:

  • Database: /data/wealthfolio.db
  • Secrets: /data/secrets.json (encrypted with WF_SECRET_KEY)
# Using named volume
-v wealthfolio-data:/data

# Using bind mount (current directory)
-v "$(pwd)/wealthfolio-data:/data"

# Using bind mount (absolute path)
-v /path/to/data:/data

Ports

The default port is 8088 for the HTTP server, which serves both the API and static frontend:

# Default mapping
-p 8088:8088

# Use different host port
-p 3000:8088

# Bind to localhost only
-p 127.0.0.1:8088:8088

Configuration Reference

Wealthfolio is configured using environment variables with the WF_* prefix. All configuration is optional except for WF_SECRET_KEY.

Server Configuration

WF_LISTEN_ADDR

Default: 0.0.0.0:8080

The address and port the server will bind to.

# Listen on all interfaces (required for Docker)
WF_LISTEN_ADDR=0.0.0.0:8088

# Listen only on localhost (for non-Docker local use)
WF_LISTEN_ADDR=127.0.0.1:8080

# Use a different port
WF_LISTEN_ADDR=0.0.0.0:3000

Docker Important: When running in Docker, you must use 0.0.0.0:PORT, not 127.0.0.1. Binding to 127.0.0.1 inside the container makes the app only accessible from within the container itself, not from your host machine.

WF_DB_PATH

Default: ./db/app.db

Path to the SQLite database file or directory. If you provide a directory path, Wealthfolio will create app.db inside it.

# Specify a file path
WF_DB_PATH=/data/wealthfolio.db

# Specify a directory (app.db will be created inside)
WF_DB_PATH=/data

# Relative path
WF_DB_PATH=./database/app.db

When using Docker, mount a volume to the database path to persist your data across container restarts.

WF_STATIC_DIR

Default: dist

Directory for serving static frontend assets. This is typically only needed if you’re building a custom frontend.

WF_STATIC_DIR=/app/public

Security Configuration

WF_SECRET_KEY

Required

A 32-byte secret key used for:

  • Encrypting sensitive data (API keys, credentials)
  • Signing JWT tokens for authentication
# Generate using OpenSSL
WF_SECRET_KEY=$(openssl rand -base64 32)

Never share or commit your secret key! If compromised, generate a new one immediately. Changing the secret key will invalidate all existing sessions and may require re-entering encrypted credentials.

WF_SECRET_FILE

Default: <data-root>/secrets.json

Path to the file where encrypted secrets are stored. By default, this is derived from your database path.

WF_SECRET_FILE=/secure/location/secrets.json

Authentication Configuration

WF_AUTH_PASSWORD_HASH

Optional (Required for Web Mode)

An Argon2id PHC string that sets the login password for accessing Wealthfolio. This is the password users will enter to log into the application when running in web mode.

# Generate a password hash using argon2 CLI or similar tool
WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...'

How to generate a password hash:

# Install argon2 (macOS with Homebrew)
brew install argon2

# Generate hash for your desired password
echo -n "your-password-here" | argon2 somesalt -e

# Or use online tools (ensure they're trustworthy)

Important: When WF_AUTH_PASSWORD_HASH is set, users will be required to enter this password to access Wealthfolio. Keep this hash secure and use a strong password. Without this setting, Wealthfolio runs without authentication (suitable only for desktop/local use).

WF_AUTH_TOKEN_TTL_MINUTES

Default: 60

JWT access token expiry time in minutes. After this time, users will need to re-authenticate.

# 1 hour (default)
WF_AUTH_TOKEN_TTL_MINUTES=60

# 24 hours
WF_AUTH_TOKEN_TTL_MINUTES=1440

# 7 days
WF_AUTH_TOKEN_TTL_MINUTES=10080

Network Configuration

WF_CORS_ALLOW_ORIGINS

Default: *

Comma-separated list of allowed CORS origins. This controls which domains can make requests to your Wealthfolio instance.

# Allow all origins (default, not recommended for production)
WF_CORS_ALLOW_ORIGINS=*

# Allow specific origins
WF_CORS_ALLOW_ORIGINS=http://localhost:1420,http://localhost:3000

# Production example
WF_CORS_ALLOW_ORIGINS=https://portfolio.example.com

In production, always specify explicit origins instead of using * to prevent unauthorized access.

WF_REQUEST_TIMEOUT_MS

Default: 30000 (30 seconds)

Request timeout in milliseconds. Requests taking longer than this will be terminated.

# 30 seconds (default)
WF_REQUEST_TIMEOUT_MS=30000

# 60 seconds for slower connections
WF_REQUEST_TIMEOUT_MS=60000

Add-ons Configuration

WF_ADDONS_DIR

Optional

Path to the directory where Wealthfolio add-ons are stored. By default, this is derived from your database path.

WF_ADDONS_DIR=/data/addons

Complete Configuration Example

Here’s a complete .env.docker file example for a production deployment:

# Server Configuration (must use 0.0.0.0 for Docker)
WF_LISTEN_ADDR=0.0.0.0:8088
WF_DB_PATH=/data/wealthfolio.db
WF_STATIC_DIR=dist

# Security (Required)
WF_SECRET_KEY=your-secure-32-byte-secret-key-here
WF_SECRET_FILE=/data/secrets.json

# Authentication (Required for web access - this is your login password!)
# Generate using: echo -n "your-password" | argon2 somesalt -e
WF_AUTH_PASSWORD_HASH='$argon2id$v=19$m=19456,t=2,p=1$...'
WF_AUTH_TOKEN_TTL_MINUTES=480

# Network
WF_CORS_ALLOW_ORIGINS=https://portfolio.example.com
WF_REQUEST_TIMEOUT_MS=30000

# Add-ons
WF_ADDONS_DIR=/data/addons

Generate the secret key automatically:

echo "WF_SECRET_KEY=$(openssl rand -base64 32)" >> .env.docker

Docker Compose

For easier management, you can use Docker Compose. Create a docker-compose.yml file:

version: '3.8'

services:
  wealthfolio:
    image: afadil/wealthfolio:latest
    container_name: wealthfolio
    ports:
      - "8088:8088"
    volumes:
      - wealthfolio-data:/data
    env_file:
      - .env.docker
    restart: unless-stopped

volumes:
  wealthfolio-data:

Then start with:

docker-compose up -d

View logs:

docker-compose logs -f

Stop the container:

docker-compose down

Deployment Best Practices

Data Persistence

Always use Docker volumes or bind mounts to persist your data:

# Named volume (recommended)
docker run -v wealthfolio-data:/data ...

# Bind mount to specific directory
docker run -v /path/to/data:/data ...

Backups

Regularly backup your data directory, which contains:

  • SQLite database (app.db)
  • Encrypted secrets (secrets.json)
  • Add-ons (if configured)
# Backup command example
docker run --rm \
  -v wealthfolio-data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/wealthfolio-backup-$(date +%Y%m%d).tar.gz /data

Security Recommendations

  1. Always use a strong secret key - Generate with openssl rand -base64 32
  2. Restrict CORS origins - Never use * in production
  3. Use HTTPS - Deploy behind a reverse proxy with SSL/TLS
  4. Keep your image updated - Regularly pull the latest version
  5. Secure your .env file - Set appropriate file permissions (chmod 600)

Reverse Proxy Setup

For production deployments, use a reverse proxy like Nginx or Caddy:

Nginx Example:

server {
    listen 443 ssl http2;
    server_name portfolio.example.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Caddy Example:

portfolio.example.com {
    reverse_proxy localhost:8080
}

Troubleshooting

Container won’t start

Check the logs:

docker logs wealthfolio

Common issues:

  • Missing WF_SECRET_KEY - Make sure it’s set in your .env file
  • Port already in use - Change the port mapping: -p 8081:8080
  • Permission issues - Ensure the data volume is writable

Cannot connect to instance

  1. Verify the container is running: docker ps
  2. Check port mapping: docker port wealthfolio
  3. Ensure firewall allows the port
  4. Check CORS settings if accessing from a different domain

Database errors

  • Ensure the data volume is properly mounted
  • Check file permissions on the database directory
  • Verify there’s enough disk space

Updating

To update to the latest version:

# Pull the latest image
docker pull afadil/wealthfolio:latest

# Stop and remove the old container
docker stop wealthfolio
docker rm wealthfolio

# Start a new container with the same configuration
docker run -d \
  -p 8080:8080 \
  -v wealthfolio-data:/data \
  --env-file .env \
  --name wealthfolio \
  afadil/wealthfolio:latest

Always backup your data before updating!

Getting Help

Next Steps