Self-Hosting Guide
Complete guide to deploying Budgero on your own infrastructure with Docker, native binaries, environment configuration, and optional integrations.
In this guide
- Deploy Budgero using Docker or native binaries on any platform.
- Zero configuration for localhost - admin account auto-created on first run.
- Set WEBSOCKET_ALLOWED_ORIGINS when accessing Budgero via a LAN IP or domain - sync requires it.
- Optionally configure currency conversion API for multi-currency support.
This guide walks you through deploying Budgero on your own infrastructure. Whether you prefer Docker containers or native binaries, you'll have a fully functional budget server running in minutes.
Deployment options
Budgero self-hosted can be deployed three ways:
- Docker - Recommended for most users. Single command, works on any platform with Docker.
- Native binary - Direct install on macOS, Linux, or Windows. Ideal for minimal setups or when Docker isn't available.
- Docker Compose - Best for production deployments with persistent storage and easy updates.
Quick start with Docker
docker run -d \ --name budgero \ -p 127.0.0.1:3001:3001 \ -v budgero_data:/data \ budgero/budgeroOn first startup, check the logs for your admin credentials:
docker logs budgero Admin account created:
Username: admin
Password: <randomly-generated-password>
⚠️ Save this password now - it will NOT be shown again.
- App:
http://localhost:3001 - Admin UI:
http://localhost:3001/admin
Use a reverse proxy (Caddy/nginx) if you need external access with HTTPS.
The quick-start command binds to 127.0.0.1, so it works out of the box. If you access Budgero from any other origin — a LAN IP, a hostname, or a domain behind a reverse proxy — you must also set WEBSOCKET_ALLOWED_ORIGINS (see WebSocket origins), or real-time sync will not connect.
Budgero persists all self-host data under /data:
- Metadata DB:
/data/budgero.db - Encrypted budget blobs:
/data/budget_spaces/
Self-hosting is free, and donations are optional. If Budgero is useful to you, make a one-time donation to help support development.
Docker Compose
services: budgero: image: budgero/budgero:latest ports: - "127.0.0.1:3001:3001" # environment: # # Required if you access Budgero from anywhere other than localhost: # - WEBSOCKET_ALLOWED_ORIGINS=http://192.168.1.50:3001 volumes: - budgero_data:/data restart: unless-stoppedvolumes: budgero_data:docker compose up -ddocker compose logs budgero # get admin credentials on first runWith Caddy for HTTPS
services: budgero: image: budgero/budgero:latest expose: - "3001" environment: - WEBSOCKET_ALLOWED_ORIGINS=https://budget.yourdomain.com volumes: - budgero_data:/data restart: unless-stopped caddy: image: caddy:2-alpine ports: - "80:80" - "443:443" volumes: - ./Caddyfile:/etc/caddy/Caddyfile:ro - caddy_data:/data restart: unless-stoppedvolumes: budgero_data: caddy_data:# Caddyfile
budget.yourdomain.com {
reverse_proxy budgero:3001
}
Native binary installation
macOS and Linux
curl -fsSL https://budgero.app/install.sh | bashWindows (PowerShell)
irm https://budgero.app/install.ps1 | iexAfter installation, start the server:
budgero serveThe server runs on port 3001 by default. Your database is stored in ./data/budgero.db.
WebSocket origins (required for non-localhost access)
Budgero syncs your budget in real time over a WebSocket, and the server only accepts WebSocket connections from origins it knows. Out of the box, only localhost / 127.0.0.1 origins are allowed. If you open Budgero from anything else — a LAN IP like http://192.168.1.50:3001, or https://budget.yourdomain.com behind a reverse proxy — the sync connection is rejected and the app cannot load your budget.
Set WEBSOCKET_ALLOWED_ORIGINS to the exact origin(s) you use in the browser, comma-separated:
# Single origin (reverse proxy with HTTPS)WEBSOCKET_ALLOWED_ORIGINS=https://budget.yourdomain.com# Multiple origins (domain + direct LAN access)WEBSOCKET_ALLOWED_ORIGINS=https://budget.yourdomain.com,http://192.168.1.50:3001Each entry must match the browser's origin exactly: scheme, host, and port (when non-default). Wildcards are not supported. If it's missing or mismatched, the server logs WebSocket connection rejected due to CORS with the origin it saw and the list it allowed — copy the rejected origin from that log line verbatim.
Environment variables
| Variable | Default | Description |
|---|---|---|
PORT | 3001 | HTTP server port |
DB_PATH | data/budgero.db | SQLite database file path |
WEBSOCKET_ALLOWED_ORIGINS | localhost origins only | Comma-separated list of browser origins allowed to connect to sync. Required for any non-localhost access — see WebSocket origins |
LOG_LEVEL | info | debug, info, warn, error |
CURRENCY_API_BASE_URL | public jsDelivr CDN | Optional: base URL of a self-hosted exchange-rate mirror — see Currency conversion |
UPDATE_CHECK_DISABLED | false | Set true to disable the update check — see Update check |
Update check
When someone opens the app, the server checks whether a newer Budgero release exists — at most once every 12 hours, cached in between. The request to budgero.app carries exactly three values: your install's version, its build sha, and the string selfhost. No instance ID, no user data, no cookies. We aggregate these into daily version counters to know roughly how many installs exist and which versions are in use — nothing per-instance is stored.
Set UPDATE_CHECK_DISABLED=true to turn it off completely; the app then makes no unsolicited outbound calls. Air-gapped installs need no configuration — a failed check is cached quietly and the app just behaves as if it's up to date.
First-run admin setup
On first startup (when no users exist), Budgero automatically creates an admin account with a random password and prints it once:
- Docker:
docker logs budgero - Foreground: Prints directly to your terminal
- Daemon mode: Check
data/logs/<name>.log
Currency conversion
Multi-currency conversion works out of the box — no API key, no signup. Rates come from the open exchange-api dataset (~350 currencies including crypto, updated daily) via the public jsDelivr CDN, and your server caches them locally so repeated requests never leave your machine.
If you want zero third-party calls, mirror the dataset's static JSON files (a daily cron copying currencies/*.min.json is enough) and point Budgero at your mirror:
CURRENCY_API_BASE_URL=https://rates.example.com/{date}/v1The {date} placeholder is substituted with the dataset date (YYYY-MM-DD). Single-currency users can ignore all of this — Budgero works fine without it.
User management
Admin UI
Access the admin dashboard at /admin to manage users, view activity, and configure settings through a web interface.
CLI
Alternatively, manage users via command line:
# Create a userbudgero admin create-user --username johndoe --name "John" --password "secret"# List all usersbudgero admin list-users# Reset a passwordbudgero admin reset-password --username johndoe --password "new-password"# Block a userbudgero admin block-user --username johndoeDisable public sign-ups
In the self-host admin dashboard at /admin, use Registration → Allow public
sign-ups. This toggle shares the CLI setting and applies immediately. If
DISABLE_REGISTRATION=true is set, the toggle is locked until you remove the
environment flag and restart the server.
Use the admin CLI to close public registration. Existing users can still sign in,
and admins can still create accounts with budgero admin create-user.
budgero admin registration disablebudgero admin registration statusbudgero admin registration enableChanges take effect without restarting the server. When disabled, the login page hides Sign up, sign-up links redirect to sign-in, and the registration API rejects new accounts. An already-open page updates on refocus or reload; API enforcement applies to new registration requests immediately.
Run the command with the same DB_PATH and working directory as the server.
The setting persists in <database-path>.registration-disabled; include this file
in backups. For Docker, run the command inside the server container, for example
docker compose exec budgero budgero admin registration disable.
Alternatively, set DISABLE_REGISTRATION=true in the server environment and
restart. This environment flag takes precedence over the CLI setting; remove it
and restart before using the CLI to reopen registration.
Running as a background service
Using the built-in daemon (all platforms)
budgero daemon start --port 3001 --name productionCheck running daemons:
budgero daemon listStop a daemon:
budgero daemon stop productionUsing systemd (Linux)
Create /etc/systemd/system/budgero.service:
[Unit]Description=Budgero Budget ServerAfter=network.target[Service]Type=simpleUser=budgeroWorkingDirectory=/opt/budgeroExecStart=/opt/budgero/budgero serveRestart=alwaysRestartSec=5[Install]WantedBy=multi-user.targetEnable and start:
sudo systemctl enable budgerosudo systemctl start budgeroUpdating
Docker
docker pull budgero/budgero:latestdocker compose downdocker compose up -dNative binary
budgero updateThis checks for the latest release and replaces the binary automatically.
Reverse proxy setup
For production, run Budgero behind a reverse proxy like nginx or Caddy for HTTPS.
Remember to set WEBSOCKET_ALLOWED_ORIGINS on the Budgero server to the public origin the proxy serves (e.g. https://budget.yourdomain.com), and make sure the proxy forwards WebSocket upgrades (both configs below do).
Caddy (automatic HTTPS)
budget.yourdomain.com {
reverse_proxy localhost:3001
}
nginx
server { listen 443 ssl http2; server_name budget.yourdomain.com; ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; location / { proxy_pass http://localhost:3001; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }}Troubleshooting
App loads but the budget never appears (sync won't connect)
If you can log in but the app hangs loading your budget — or devices stop seeing each other's changes — the sync WebSocket is almost certainly being rejected. Check the server logs for:
WebSocket connection rejected due to CORS
The log line includes the origin the browser sent and the origins the server allowed. Add the rejected origin to WEBSOCKET_ALLOWED_ORIGINS exactly as logged (scheme, host, and port must all match) and restart the server.
Database locked errors
SQLite doesn't handle concurrent writes well. If you see lock errors:
- Ensure only one Budgero instance is running
- Check that
DB_PATHpoints to a local filesystem (not a network share)
Port already in use
Change the port with PORT=4000 or --port 4000.
Container won't start
Check logs with docker logs budgero. Common issues:
- Volume permissions (ensure the container can write to
/data) - Port conflicts (another service using port 3001)
FAQ
- Do I need to set up a database? No. Budgero uses SQLite plus encrypted blob files. Just mount
/dataand Budgero handles the rest. - Can I migrate from Budgero Cloud to self-hosted? Yes. Export your data from Cloud and import it into your self-hosted instance.
- Is there a mobile app? Access your self-hosted instance from any browser. Add it to your home screen for an app-like experience.
- How do I back up my data? Back up the entire
/datavolume (includesbudgero.dbandbudget_spaces/).