Getting started
Spotcraft runs on two servers: an apps server for your apps and an ops server for the dashboard and monitoring, joined by a private network. You deploy Spotcraft itself from GitHub Actions, then add apps from the dashboard.
You need: a GitHub account, a Docker Hub account if you publish your own images, two Debian/Ubuntu servers (or a Hetzner Cloud account), and a domain for the dashboard.
1. Fork the repository
Fork vindotski/spotcraft. Everything personal goes into your fork's settings, never into its files.
2. Create the servers
On Hetzner Cloud, the OpenTofu module in infra/hetzner creates both servers (ARM, Debian 13),
the private network, a firewall and the deploy key:
ssh-keygen -t ed25519 -N "" -C spotcraft-deploy -f ~/.ssh/spotcraft_deploy
cd infra/hetzner
printf 'deploy_ssh_public_key = "%s"\n' "$(cat ~/.ssh/spotcraft_deploy.pub)" > terraform.tfvars
export HCLOUD_TOKEN=... # a Hetzner project token with read and write access
tofu init && tofu apply
It prints both servers' addresses. Keep terraform.tfstate somewhere safe.
On any other provider, create two Debian 13 (or 12) or Ubuntu 24.04 servers with a private
network between them (10.0.0.2 for apps and 10.0.0.3 for ops by default), and give each the
first-boot setup from infra/cloud-init.yaml as user data, with its placeholders filled in.
Either way, wait a few minutes for the first boot, then check that both servers are ready:
ssh -i ~/.ssh/spotcraft_deploy deploy@<server ip> cat /var/log/spotcraft-ready
3. Point the dashboard's domain
Create A and AAAA records for the dashboard's domain (e.g. dash.example.com) pointing at the
ops server. If your DNS is on Cloudflare, leave the records DNS only (not proxied): Caddy
gets the certificate itself, and the dashboard needs visitors' real addresses for its login
lockout.
4. Choose the images
Use the published images (vindotski/spotcraft-agent and vindotski/spotcraft-dashboard), or
publish your own: set the repository variable DOCKERHUB_USERNAME and the secret
DOCKERHUB_TOKEN (a Docker Hub access token with read and write access), then push to main.
CI builds both images for amd64 and arm64.
5. Create the GitHub environment
In your fork's Settings → Environments, create production with:
| Kind | Name | Value |
|---|---|---|
| variable | APPS_HOST |
apps server public IP |
| variable | OPS_HOST |
ops server public IP |
| variable | DASHBOARD_DOMAIN |
e.g. dash.example.com |
| secret | DEPLOY_SSH_KEY |
the deploy user's private key (~/.ssh/spotcraft_deploy) |
| secret | KNOWN_HOSTS |
ssh-keyscan -t ed25519 <apps ip> <ops ip> (check the fingerprints) |
| secret | AGENT_TOKEN |
openssl rand -hex 32 |
| secret | SESSION_SECRET |
openssl rand -hex 32 |
| secret | DASHBOARD_PASSWORD_HASH |
pnpm --filter @spotcraft/dashboard hash-password |
APPS_PRIVATE_IP and OPS_PRIVATE_IP are optional variables, for private addresses other
than 10.0.0.2 and 10.0.0.3.
6. Deploy Spotcraft
Run the Deploy workflow (Actions → Deploy → Run workflow) with a version, such as
0.1.0. It deploys the apps server, then the ops server, waits until every service is healthy,
and checks the dashboard from outside. Then log in at your dashboard's domain.
7. Add your first app
In the dashboard, choose Add app and fill in:
- Name: lower-case letters, digits and dashes, e.g.
blog. - Image and tag: a public image on Docker Hub, e.g.
you/blogand1.0.0. Recent tags are suggested. - Domain: e.g.
blog.example.com. Point its A and AAAA records at the apps server (DNS only, if on Cloudflare). A root domain likeexample.comalso getswww.redirected. - Container port, memory and CPU limits, and environment variables.
Deploy pulls the image, waits until the app is healthy and switches traffic to it. If anything fails, you see why, and the previous version keeps running. The certificate arrives on the first HTTPS request once DNS points at the apps server.
Apps run with a read-only filesystem by default; turn it off for apps that write outside
/tmp.
Upgrading and rolling back
To upgrade Spotcraft, read the changelog for the new version, then run the Deploy workflow with it. To roll back, run it with the previous version. Your apps keep running throughout; they are only restarted when a release says so.
To update an app, edit it in the dashboard and pick the new tag. To roll an app back, pick the old tag.
Next
- Security model: what Spotcraft trusts and the risks it accepts.
- Decision records: why it's built the way it is.