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:
- Environment file (recommended for production) - using
--env-fileflag - Inline environment variables - using
-eflag (simpler for testing)
Option 1: Create an Environment File (Recommended)
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
Using Environment File (Recommended)
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
Production with Encryption (Recommended)
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 withWF_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
- Always use a strong secret key - Generate with
openssl rand -base64 32 - Restrict CORS origins - Never use
*in production - Use HTTPS - Deploy behind a reverse proxy with SSL/TLS
- Keep your image updated - Regularly pull the latest version
- Secure your
.envfile - 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.envfile - Port already in use - Change the port mapping:
-p 8081:8080 - Permission issues - Ensure the data volume is writable
Cannot connect to instance
- Verify the container is running:
docker ps - Check port mapping:
docker port wealthfolio - Ensure firewall allows the port
- 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
- Documentation: wealthfolio.app/docs
- Discord Community: discord.gg/WDMCY6aPWK
- GitHub Issues: github.com/afadil/wealthfolio/issues
Next Steps
- Configure Add-ons - Extend Wealthfolio with custom functionality