hantim-server
Server provisioning and app management for the hantim webserver. This repo
lives at /opt/hantim on the server and contains Docker Compose configs, deploy
scripts, nginx configs, and Gitea Actions workflows for each app.
Repo structure
docker/
nginx/ # Reverse proxy (nginx:alpine) + SSL config
nginx.conf # Main nginx config
conf.d/ # Per-app server blocks (e.g. example.com.conf)
compose.yml
<domain>/ # Per-app static site (e.g. example.com)
compose.yml
garage/ # Garage S3-compatible object storage
compose.yml
garage.toml.template
scripts/
bootstrap.sh # First-time server setup (manual, uses Bitwarden)
configure.sh # Idempotent server config (CI-safe)
deploy.sh # Deploy + cert issuance + provision (called via SSH)
tools/
new-app.sh # Add a new static site (run from dev machine)
new-service.sh # Add a new Docker service (run from dev machine)
remove-app.sh # Remove a static site (run from dev machine)
.gitea/workflows/ # Per-app deploy workflows triggered by path changes
Provisioning a new server
On a fresh Rocky Linux 9 install:
dnf install -y git
git clone https://git.timothykim.net/hantim/hantim-server.git /opt/hantim
bash /opt/hantim/scripts/bootstrap.sh
bootstrap.sh runs once manually. It:
- Installs system dependencies (jq, certbot, unzip, Docker)
- Installs the
bwsCLI (Bitwarden Secrets Manager) - Prompts for a Bitwarden Secrets Manager access token (saved to
/etc/bws-token) - Fetches the Docker registry token and deploy SSH public key via
bws - Logs into the Gitea Docker registry
- Creates the
deployuser with restricted SSH access and sudo - Runs
configure.sh(firewall, certbot, start services)
configure.sh is idempotent and CI-safe. It:
- Opens firewall ports (HTTP, HTTPS, Garage RPC)
- Sets up certbot and issues SSL certificates for all configured domains
- Starts all services (nginx first, then all apps)
Prerequisites:
- DNS for all configured domains must point to the server
- Bitwarden Secrets Manager access token for the
hantim-servermachine account
Adding a new app
./tools/new-app.sh <domain>
Example: ./tools/new-app.sh example.com
This single command handles everything: creates DNS records on Vultr, creates
the Gitea repo from static-site-template, generates all config files, issues
the SSL cert (zero downtime), commits and pushes, triggers the first build,
and verifies the site is live.
Prerequisites: domain nameservers pointed to Vultr, bws/jq/dig
installed. Uses the deploy SSH key from Bitwarden Secrets Manager -- no
personal server access needed. See USECASES.md for full details.
After the site is live, clone the app repo and customize it:
git clone git@git.timothykim.net:hantim/<domain>.git
cd <domain>
# edit content
git add . && git commit -m "initial content" && git push
Deploying changes
App code change (push to an app repo like example.com):
- The app's
build.ymlbuilds the Docker image, pushes to the registry, and SSHes into the server to trigger a deploy
Compose or nginx config change (push to this repo):
- Gitea Actions workflows trigger based on which
docker/<app>/paths changed deploy.shpulls the latest code and runsdocker compose up -d- For nginx deploys, the config is tested before applying to prevent downtime
Restoring from backup
Run the same three commands as provisioning. bootstrap.sh is idempotent:
- Existing packages are skipped
- Existing certs are skipped (or re-issued if the server is new)
- All services are started
Secrets
Bitwarden Secrets Manager (project: hantim, fetched via bws CLI on the server):
| Secret | Purpose | Used by |
|---|---|---|
hantim-ci-registry-push |
Gitea token for Docker registry | bootstrap.sh |
hantim-deploy-ssh-public-key |
Deploy user's SSH public key | bootstrap.sh |
hantim-deploy-ssh-private-key |
Deploy user's SSH private key | new-app.sh |
hantim-new-app-script |
Gitea API token for creating repos | new-app.sh |
hantim-vultr-api-key |
Vultr API key for DNS management | new-app.sh |
Machine accounts: hantim-server (token at /etc/bws-token), hantim-ci (reserved).
Gitea org-level (hantim) secrets/variables:
| Name | Type | Purpose |
|---|---|---|
DEPLOY_HOST |
Variable | Server IP or hostname |
DEPLOY_SSH_KEY |
Secret | SSH private key for the deploy user |
CI_REGISTRY_TOKEN |
Secret | Gitea token for Docker registry login |