Skip to content

Deployment

This guide covers production deployment on Unraid using Docker Compose, with a Cloudflare tunnel for HTTPS and GitHub Actions for automatic deploys.

Architecture

git push → GitHub Actions → SSH (Tailscale) → Unraid server

                                          docker compose up --build

                                       [geekonomics container :3001]

                                       [cloudflared tunnel container]

                                       your-domain.com (HTTPS)

Cloudflare tunnel setup

A Cloudflare tunnel exposes your container to the internet without opening ports on your router.

1. Create the tunnel

bash
cloudflared tunnel login
cloudflared tunnel create geekonomics

This generates a credentials file at ~/.cloudflared/<tunnel-id>.json. Copy it into the repo:

bash
cp ~/.cloudflared/<tunnel-id>.json /path/to/geekonomics-app/cloudflared/

2. Edit cloudflared/config.yml

yaml
tunnel: <your-tunnel-id>
credentials-file: /etc/cloudflared/<your-tunnel-id>.json

ingress:
  - hostname: books.yourdomain.com
    service: http://bookkeeping:3001
  - service: http_status:404

3. Add a CNAME in Cloudflare DNS

FieldValue
TypeCNAME
Namebooks (or your subdomain)
Target<tunnel-id>.cfargotunnel.com
ProxyEnabled (orange cloud)

GitHub Actions automatic deploys

Every push to main SSHes into your Unraid server and runs git pull && docker compose up -d --build.

Deploy key setup

The Unraid server needs its own SSH key registered as a GitHub deploy key (one key per repo — GitHub rejects reuse).

On your Unraid server:

bash
ssh-keygen -t ed25519 -C "geekonomics-deploy" -f ~/.ssh/geekonomics_deploy -N ""
cat ~/.ssh/geekonomics_deploy.pub   # copy this

Add the public key in GitHub: Settings → Deploy keys → Add deploy key (read-only).

Add an SSH config alias so git picks the right key:

bash
cat >> ~/.ssh/config << 'EOF'

Host github-geekonomics
  HostName github.com
  User git
  IdentityFile ~/.ssh/geekonomics_deploy
EOF

Set the remote to use the alias:

bash
cd /path/to/geekonomics-app
git remote set-url origin git@github-geekonomics:oneofthegeeks/geekonomics-app.git

GitHub Actions secrets

Go to Settings → Secrets and variables → Actions and add:

SecretValue
DEPLOY_HOSTYour server's Tailscale IP (e.g. 100.x.x.x) — not the LAN IP
DEPLOY_USERroot
DEPLOY_SSH_KEYYour dev machine's private key (the one in the server's authorized_keys)
DEPLOY_PORT22
DEPLOY_PATH/mnt/user/appdata/geekonomics-app
TS_OAUTH_CLIENT_IDTailscale OAuth client ID
TS_OAUTH_CLIENT_SECRETTailscale OAuth client secret

Use the Tailscale IP

DEPLOY_HOST must be the Tailscale IP (100.x.x.x), not the LAN IP. The GitHub Actions runner joins Tailscale and routes via that network — it cannot reach your LAN IP directly.

Get your Tailscale IP at login.tailscale.com or by running tailscale ip on the server.

Tailscale OAuth credentials

Go to login.tailscale.com/admin/settings/oauth and create a new OAuth client with devices:write scope. Use the client ID and secret for TS_OAUTH_CLIENT_ID and TS_OAUTH_CLIENT_SECRET.

Manual deploy

If you need to deploy without waiting for GitHub Actions:

bash
ssh root@your-server-tailscale-ip
cd /mnt/user/appdata/geekonomics-app
git pull origin main
docker compose up -d --build
docker image prune -f

Deploy time is typically 2–3 minutes — the Vite frontend build is the slow part.

Data persistence

Your data lives in bind mounts outside the container and survives rebuilds:

geekonomics-app/
├── data/geekonomics.db   — SQLite database
├── data/sessions.db      — Session store
└── uploads/              — Receipt attachments

Never delete these directories. Rebuilding the container image is safe; deleting the bind-mount directories is not.

Geekonomics — self-hosted bookkeeping for small businesses