5 KiB
First-Time Deployment
This guide walks through every manual step needed on a fresh server before the CI/CD workflow can auto-deploy.
After completing this once, all future deploys happen automatically when you push a v* tag.
Prerequisites
- A Linux server (Debian/Ubuntu assumed, adjust package commands for other distros)
- Root or sudo access
- A domain name pointed at the server's IP
- PostgreSQL 15+ installed
- Caddy installed
Step 1: Create the system user
Create a dedicated budgit user for deployment:
sudo useradd --create-home --shell /bin/bash budgit
Grant the deploy user the specific sudo permissions it needs (no password):
sudo tee /etc/sudoers.d/budgit> /dev/null << 'EOF'
deploy ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart budgit, /usr/bin/systemctl status budgit
EOF
sudo chmod 440 /etc/sudoers.d/budgit
Step 2: Create the application directory
sudo mkdir -p /opt/budgit
sudo chown budgit:budgit /opt/budgit
sudo chmod 750 /opt/budgit
Step 3: Set up PostgreSQL
Follow docs/database-setup.md in full. By the end you should have:
- A
budgit-adminPostgreSQL role - A
budgitdatabase owned bybudgit-admin pg_hba.confpeer auth with an ident map so thebudgitsystem user authenticates asbudgit-admin
Verify it works:
sudo -u budgit psql -U budgit-admin -d budgit -c "SELECT 1;"
Step 4: Create the environment file
sudo -u budgit tee /opt/budgit/.env > /dev/null << 'EOF'
APP_ENV=production
APP_URL=https://budgit.now
HOST=127.0.0.1
PORT=9000
DB_DRIVER=pgx
DB_CONNECTION=postgres://budgit-admin@/budgit?host=/run/postgresql&sslmode=disable
JWT_SECRET=<run: openssl rand -base64 32>
MAILER_SMTP_HOST=
MAILER_SMTP_PORT=587
MAILER_IMAP_HOST=
MAILER_IMAP_PORT=993
MAILER_USERNAME=
MAILER_PASSWORD=
MAILER_EMAIL_FROM=
SUPPORT_EMAIL=
EOF
Generate and fill in the JWT_SECRET:
openssl rand -base64 32
Fill in the mailer variables if email is configured. Lock down permissions:
sudo chmod 600 /opt/budgit/.env
Step 5: Do the initial binary deploy
Build locally (or on any machine with Go + Tailwind + Task installed):
task build
Copy the binary to the server:
scp ./dist/budgit your-user@your-server:/tmp/budgit
ssh your-user@your-server "sudo mv /tmp/budgit /opt/budgit/budgit && sudo chown budgit:budgit /opt/budgit/budgit && sudo chmod 755 /opt/budgit/budgit"
Step 6: Install the systemd service
Copy the unit file from this repo:
scp docs/budgit.service your-user@your-server:/tmp/budgit.service
ssh your-user@your-server "sudo mv /tmp/budgit.service /etc/systemd/system/budgit.service"
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable budgit
sudo systemctl start budgit
Check it's running:
sudo systemctl status budgit
curl http://127.0.0.1:9000/healthz
You should see ok.
Step 7: Configure Caddy
Replace the existing budgit.now site block in your Caddyfile (typically /etc/caddy/Caddyfile).
Before (static file server):
budgit.now, www.budgit.now, mta-sts.budgit.now, autodiscover.budgit.now {
import common_headers
import budgit_now_ssl
root * /var/www/budgit.now
file_server
}
After (split app from other subdomains):
budgit.now, www.budgit.now {
import common_headers
import budgit_now_ssl
encode gzip zstd
handle /.well-known/* {
root * /var/www/budgit.now
file_server
}
handle {
reverse_proxy 127.0.0.1:9000 {
health_uri /healthz
health_interval 10s
health_timeout 3s
}
}
}
mta-sts.budgit.now, autodiscover.budgit.now {
import common_headers
import budgit_now_ssl
root * /var/www/budgit.now
file_server
}
Reload Caddy:
sudo systemctl reload caddy
Verify the public endpoint:
curl https://budgit.now/healthz
Step 8: Configure Forgejo secrets
In your Forgejo repository, go to Settings > Secrets and add:
| Secret | Value |
|---|---|
SSH_KEY |
Contents of deploy_key (the private key) |
SSH_USER |
deploy |
SSH_HOST |
Your server's IP or hostname |
DEPLOY_PATH |
/opt/budgit |
APP_URL |
https://budgit.now |
Step 9: Verify auto-deploy
Tag and push to trigger the workflow:
git tag v0.1.0
git push origin v0.1.0
Watch the workflow in Forgejo's Actions tab. It should:
- Build the binary with the version baked in
- SCP it to the server
- Restart the service
- Pass the health check
Confirm the version is running:
journalctl -u budgit --no-pager -n 5
You should see a log line like server starting version=v0.1.0.
Summary
After completing these steps, the deployment flow is:
git tag v1.2.3 && git push origin v1.2.3
-> Forgejo workflow triggers
-> Builds binary with version embedded
-> SCPs to server, restarts systemd
-> Health check verifies
-> Auto-rollback on failure
No further manual steps are needed for subsequent deploys.