Skip to main content

Reverse Proxy Setup

Deploy BillManager behind a reverse proxy for HTTPS and production use.

Why a Reverse Proxy?

For production deployments, you should:

  • Enable HTTPS - Encrypt all traffic with SSL/TLS certificates
  • Use a Custom Domain - Access BillManager at bills.yourdomain.com
  • Add Security Headers - Improve security with proper HTTP headers
  • Enable Compression - Reduce bandwidth usage

Traefik

Traefik is recommended for Docker environments with automatic HTTPS.

docker-compose.yml

services:
billmanager:
image: ghcr.io/brdweb/billmanager:latest
environment:
- DEPLOYMENT_MODE=self-hosted
- DATABASE_URL=postgresql://billsuser:password@db:5432/billmanager
- FLASK_SECRET_KEY=your-secret-key
- JWT_SECRET_KEY=your-jwt-secret-key
- APP_URL=https://bills.yourdomain.com
labels:
- "traefik.enable=true"
- "traefik.http.routers.billmanager.rule=Host(`bills.yourdomain.com`)"
- "traefik.http.routers.billmanager.entrypoints=websecure"
- "traefik.http.routers.billmanager.tls.certresolver=letsencrypt"
- "traefik.http.services.billmanager.loadbalancer.server.port=5000"
depends_on:
- db
networks:
- traefik
- internal

db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=billsuser
- POSTGRES_PASSWORD=password
- POSTGRES_DB=billmanager
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- internal

volumes:
postgres_data:

networks:
traefik:
external: true
internal:
tip

Make sure your Traefik instance is configured with a letsencrypt certificate resolver. See Traefik documentation for details.

Nginx

For traditional Nginx deployments:

nginx.conf

server {
listen 80;
server_name bills.yourdomain.com;
return 301 https://$server_name$request_uri;
}

server {
listen 443 ssl http2;
server_name bills.yourdomain.com;

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

# SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;

# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

location / {
proxy_pass http://localhost:5000;
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;

# WebSocket support (if needed in future)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

With Certbot (Let's Encrypt)

# Install certbot
sudo apt install certbot python3-certbot-nginx

# Get certificate
sudo certbot --nginx -d bills.yourdomain.com

# Auto-renewal is set up automatically

Caddy

Caddy provides automatic HTTPS with minimal configuration:

Caddyfile

bills.yourdomain.com {
reverse_proxy localhost:5000
}

docker-compose.yml with Caddy

services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped

billmanager:
image: ghcr.io/brdweb/billmanager:latest
environment:
- DEPLOYMENT_MODE=self-hosted
- DATABASE_URL=postgresql://billsuser:password@db:5432/billmanager
- FLASK_SECRET_KEY=your-secret-key
- APP_URL=https://bills.yourdomain.com

volumes:
caddy_data:
caddy_config:
postgres_data:

Important: Update APP_URL

When using a reverse proxy with HTTPS, make sure to update APP_URL:

environment:
- APP_URL=https://bills.yourdomain.com

This ensures email links point to the correct URL.