Back to docsGuide9 min read

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

Bash
docker run -d \  --name budgero \  -p 127.0.0.1:3001:3001 \  -v budgero_data:/data \  budgero/budgero

On first startup, check the logs for your admin credentials:

Bash
docker logs budgero
Code
  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/
Support Budgero

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

YAML
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:
Bash
docker compose up -ddocker compose logs budgero  # get admin credentials on first run

With Caddy for HTTPS

YAML
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:
Code
# Caddyfile
budget.yourdomain.com {
    reverse_proxy budgero:3001
}

Native binary installation

macOS and Linux

Bash
curl -fsSL https://budgero.app/install.sh | bash

Windows (PowerShell)

POWERSHELL
irm https://budgero.app/install.ps1 | iex

After installation, start the server:

Bash
budgero serve

The 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:

Bash
# 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:3001

Each 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

VariableDefaultDescription
PORT3001HTTP server port
DB_PATHdata/budgero.dbSQLite database file path
WEBSOCKET_ALLOWED_ORIGINSlocalhost origins onlyComma-separated list of browser origins allowed to connect to sync. Required for any non-localhost access — see WebSocket origins
LOG_LEVELinfodebug, info, warn, error
CURRENCY_API_BASE_URLpublic jsDelivr CDNOptional: base URL of a self-hosted exchange-rate mirror — see Currency conversion
UPDATE_CHECK_DISABLEDfalseSet 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:

Bash
CURRENCY_API_BASE_URL=https://rates.example.com/{date}/v1

The {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:

Bash
# 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 johndoe

Disable 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.

Bash
budgero admin registration disablebudgero admin registration statusbudgero admin registration enable

Changes 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)

Bash
budgero daemon start --port 3001 --name production

Check running daemons:

Bash
budgero daemon list

Stop a daemon:

Bash
budgero daemon stop production

Using systemd (Linux)

Create /etc/systemd/system/budgero.service:

INI
[Unit]Description=Budgero Budget ServerAfter=network.target[Service]Type=simpleUser=budgeroWorkingDirectory=/opt/budgeroExecStart=/opt/budgero/budgero serveRestart=alwaysRestartSec=5[Install]WantedBy=multi-user.target

Enable and start:

Bash
sudo systemctl enable budgerosudo systemctl start budgero

Updating

Docker

Bash
docker pull budgero/budgero:latestdocker compose downdocker compose up -d

Native binary

Bash
budgero update

This 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)

Code
budget.yourdomain.com {
    reverse_proxy localhost:3001
}

nginx

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:

Code
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:

  1. Ensure only one Budgero instance is running
  2. Check that DB_PATH points 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 /data and 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 /data volume (includes budgero.db and budget_spaces/).