Prerequisites
What you need before you start. The reference deployment targets DigitalOcean + DigitalOcean Spaces; if you're on a different provider, the shape is the same — substitute equivalents.
Compute
Three machines for staging is comfortable; production starts at five. The minimum useful sizes:
| Role | vCPU | RAM | Disk | Notes |
|---|---|---|---|---|
| web | 2 | 4 GB | 40 GB | Run two for HA. |
| worker | 2 | 4 GB | 40 GB | Can colocate with web for small instances. |
| postgres | 2 | 8 GB | 100 GB | Start at 100 GB; grow with disk usage. |
| backup | 1 | 2 GB | 80 GB | Idle most of the time; runs cron only. |
| monitoring | 2 | 4 GB | 60 GB | Prom + Loki + Alertmanager + Grafana. |
Ubuntu 24.04 LTS is the supported OS. Other Debian derivatives work but are not regularly tested.
Storage
shithub stores three categories:
- Bare repos — on the web/worker hosts at
/data/repos. Local filesystem; not in object storage. Sized to total push volume. - Webhook delivery bodies, LFS, attachments — in an S3-compatible object store. The reference setup uses DigitalOcean Spaces; MinIO works for self-hosted equivalents.
- Backups — separate bucket. Cross-region mirror is strongly recommended; lifecycle policies prune old data.
Database
Postgres 16 on a dedicated host. Earlier versions work in
development; production requires 16+ for the pg_stat_statements
features and JSON functions we rely on.
The archive_command ships WAL to object storage in real time
(deploy.md); a daily logical dump is taken
on top of that.
Domain + TLS
You need:
- A domain you control (e.g.
shithub.sh). - DNS records for the app (
shithub.sh) and the docs subdomain (docs.shithub.sh). - A TLS certificate. Caddy obtains and renews via Let's Encrypt automatically — no manual cert management — but the DNS records must point at your public IP first.
shithub sends transactional email (signup verification, password reset, notifications). Three backends:
stdout— prints to the journal. Dev only.smtp— talk to your own MTA or a SaaS that exposes SMTP.postmark— uses Postmark's HTTP API (the recommended production path; Postmark's deliverability is excellent for transactional mail).
Warm-up your sending domain before going live; cold IPs have their first thousand emails treated as spam.
SSH
The git server expects port 22 reachable from your users. The
sshd config (deploy/sshd_config.j2) restricts the git user to
the AKC-driven flow; the rest of sshd is operator-only.
Object store credentials
Store both an access key for the runtime (read+write to the bucket) and a separate set for backups + restore drill (read-only on the runtime bucket; full access on the backup bucket). Rotate quarterly.
Time
NTP must be running; the rate-limit windows, TOTP, and audit-log
timestamps depend on monotonic, accurate time. Ubuntu's
systemd-timesyncd is enough.
Skills you'll want
- Comfort with Ansible — the playbook is the install path.
- Postgres operational basics: pg_dump/pg_restore, monitoring, WAL.
- Reading systemd journal output (
journalctl -u …).
If any of those are unfamiliar, allow extra time on your first deploy and follow the runbooks closely.
View source
| 1 | # Prerequisites |
| 2 | |
| 3 | What you need before you start. The reference deployment targets |
| 4 | DigitalOcean + DigitalOcean Spaces; if you're on a different |
| 5 | provider, the shape is the same — substitute equivalents. |
| 6 | |
| 7 | ## Compute |
| 8 | |
| 9 | Three machines for staging is comfortable; production starts at |
| 10 | five. The minimum useful sizes: |
| 11 | |
| 12 | | Role | vCPU | RAM | Disk | Notes | |
| 13 | |--------------|-----:|-----:|-----:|------------------------------------------| |
| 14 | | web | 2 | 4 GB | 40 GB | Run two for HA. | |
| 15 | | worker | 2 | 4 GB | 40 GB | Can colocate with web for small instances. | |
| 16 | | postgres | 2 | 8 GB | 100 GB | Start at 100 GB; grow with disk usage. | |
| 17 | | backup | 1 | 2 GB | 80 GB | Idle most of the time; runs cron only. | |
| 18 | | monitoring | 2 | 4 GB | 60 GB | Prom + Loki + Alertmanager + Grafana. | |
| 19 | |
| 20 | Ubuntu 24.04 LTS is the supported OS. Other Debian derivatives |
| 21 | work but are not regularly tested. |
| 22 | |
| 23 | ## Storage |
| 24 | |
| 25 | shithub stores three categories: |
| 26 | |
| 27 | - **Bare repos** — on the web/worker hosts at `/data/repos`. Local |
| 28 | filesystem; **not** in object storage. Sized to total push |
| 29 | volume. |
| 30 | - **Webhook delivery bodies, LFS, attachments** — in an |
| 31 | S3-compatible object store. The reference setup uses |
| 32 | DigitalOcean Spaces; MinIO works for self-hosted equivalents. |
| 33 | - **Backups** — separate bucket. Cross-region mirror is strongly |
| 34 | recommended; lifecycle policies prune old data. |
| 35 | |
| 36 | ## Database |
| 37 | |
| 38 | Postgres 16 on a dedicated host. Earlier versions work in |
| 39 | development; production requires 16+ for the `pg_stat_statements` |
| 40 | features and JSON functions we rely on. |
| 41 | |
| 42 | The `archive_command` ships WAL to object storage in real time |
| 43 | ([deploy.md](./deploy.md#postgres)); a daily logical dump is taken |
| 44 | on top of that. |
| 45 | |
| 46 | ## Domain + TLS |
| 47 | |
| 48 | You need: |
| 49 | |
| 50 | - A domain you control (e.g. `shithub.sh`). |
| 51 | - DNS records for the app (`shithub.sh`) and the docs |
| 52 | subdomain (`docs.shithub.sh`). |
| 53 | - A TLS certificate. Caddy obtains and renews via Let's Encrypt |
| 54 | automatically — no manual cert management — but the DNS records |
| 55 | must point at your public IP first. |
| 56 | |
| 57 | |
| 58 | |
| 59 | shithub sends transactional email (signup verification, password |
| 60 | reset, notifications). Three backends: |
| 61 | |
| 62 | - `stdout` — prints to the journal. **Dev only.** |
| 63 | - `smtp` — talk to your own MTA or a SaaS that exposes SMTP. |
| 64 | - `postmark` — uses Postmark's HTTP API (the recommended |
| 65 | production path; Postmark's deliverability is excellent for |
| 66 | transactional mail). |
| 67 | |
| 68 | Warm-up your sending domain before going live; cold IPs have |
| 69 | their first thousand emails treated as spam. |
| 70 | |
| 71 | ## SSH |
| 72 | |
| 73 | The git server expects port 22 reachable from your users. The |
| 74 | sshd config (deploy/sshd_config.j2) restricts the `git` user to |
| 75 | the AKC-driven flow; the rest of sshd is operator-only. |
| 76 | |
| 77 | ## Object store credentials |
| 78 | |
| 79 | Store both an access key for the runtime (read+write to the |
| 80 | bucket) and a separate set for backups + restore drill (read-only |
| 81 | on the runtime bucket; full access on the backup bucket). |
| 82 | Rotate quarterly. |
| 83 | |
| 84 | ## Time |
| 85 | |
| 86 | NTP must be running; the rate-limit windows, TOTP, and audit-log |
| 87 | timestamps depend on monotonic, accurate time. Ubuntu's |
| 88 | `systemd-timesyncd` is enough. |
| 89 | |
| 90 | ## Skills you'll want |
| 91 | |
| 92 | - Comfort with Ansible — the playbook is the install path. |
| 93 | - Postgres operational basics: pg_dump/pg_restore, monitoring, |
| 94 | WAL. |
| 95 | - Reading systemd journal output (`journalctl -u …`). |
| 96 | |
| 97 | If any of those are unfamiliar, allow extra time on your first |
| 98 | deploy and follow the runbooks closely. |