Self-Hosting Guide

Deploy Vibe Ledger on your own infrastructure

Prerequisites

Backend Setup

1. Clone the repository

git clone git@github.com:JamestheDon/vibe-ledger.git /srv/fflmt

2. Create virtual environment

python3 -m venv /srv/fflmt/.venv && source /srv/fflmt/.venv/bin/activate

3. Install dependencies

pip install -r requirements.txt

4. Configure environment

Copy the example environment file and fill in your values:

cp .env.example .env

Edit .env and set the following variables:

DATABASE_URL=postgres://user:pass@localhost:5432/fflmt
SECRET_KEY=your-random-secret-key
PLAID_CLIENT_ID=your-plaid-client-id
PLAID_SECRET=your-plaid-secret
PLAID_ENV=sandbox          # or production
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
ANTHROPIC_API_KEY=sk-ant-...

5. Run migrations

python manage.py migrate

6. Create superuser

python manage.py createsuperuser

7. Run with gunicorn

gunicorn --bind 127.0.0.1:8000 --workers 2 --timeout 120 fflmt.wsgi:application

Frontend Build

1. Navigate to frontend source

cd vibe-ledger-app/

2. Install dependencies

npm install

3. Create environment file

VITE_API_URL=https://api.yourdomain.com

4. Build for production

npm run build

5. Deploy static files

Copy the dist/ contents to your static file serving directory:

cp -r dist/* /path/to/static/

Web Server (Caddy)

Caddy is recommended because it auto-provisions HTTPS via Let's Encrypt. Example Caddyfile configuration:

api.yourdomain.com {
    reverse_proxy localhost:8000
}

app.yourdomain.com {
    root * /path/to/dist
    file_server
    try_files {path} /index.html
}

yourdomain.com {
    root * /path/to/landing
    file_server
}

Caddy automatically obtains and renews TLS certificates from Let's Encrypt for all configured domains.

Systemd Service

Create a systemd unit file to run gunicorn as a managed service with auto-restart:

[Unit]
Description=Vibe Ledger (gunicorn)
After=network.target postgresql.service

[Service]
User=www-data
Group=www-data
WorkingDirectory=/srv/fflmt
Environment="PATH=/srv/fflmt/.venv/bin"
ExecStart=/srv/fflmt/.venv/bin/gunicorn \
    --bind 127.0.0.1:8000 \
    --workers 2 \
    --timeout 120 \
    fflmt.wsgi:application
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start the service:

sudo systemctl enable fflmt.service
sudo systemctl start fflmt.service

Plaid Sync (Cron/Timer)

Set up a systemd timer or cron job to keep bank transactions up to date automatically. Daily sync is recommended.

Using cron:

# Run Plaid sync daily at 6:00 AM
0 6 * * * /srv/fflmt/.venv/bin/python /srv/fflmt/manage.py plaid_sync

Or using a systemd timer, create /etc/systemd/system/fflmt-sync.timer:

[Unit]
Description=Daily Plaid sync for Vibe Ledger

[Timer]
OnCalendar=*-*-* 06:00:00
Persistent=true

[Install]
WantedBy=timers.target

And the corresponding service unit /etc/systemd/system/fflmt-sync.service:

[Unit]
Description=Vibe Ledger Plaid Sync

[Service]
User=www-data
WorkingDirectory=/srv/fflmt
Environment="PATH=/srv/fflmt/.venv/bin"
ExecStart=/srv/fflmt/.venv/bin/python manage.py plaid_sync
Type=oneshot

Key Vault / Secrets

Vibe Ledger uses a keyvault module to load secrets. In production, set environment variables or use the Django settings directly. Never commit .env or credentials files to version control.

Security

Always use HTTPS in production. Never expose Django DEBUG=True. Rotate your SECRET_KEY periodically. Keep Plaid and Stripe credentials secure.