Files
hantim-server/README.md
T
timothykim 7d5f0920b7 replace per-app deploy scripts with generic deploy.sh
Dispatch now validates the command format and routes to a single
deploy.sh that handles any app. This fixes first-deploy failures
for new apps (dispatch no longer needs a static case list) and
simplifies new-app.sh (no deploy script or dispatch update needed).

Also adds input validation to new-app.sh, set -euo pipefail to
deploy scripts, and dnf module reset before nodejs install.
2026-03-13 17:00:59 -04:00

3.1 KiB

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, and Gitea Actions workflows for each app.

Repo structure

docker/              # One directory per app, each with a compose.yml
  nginx/             # Nginx Proxy Manager (reverse proxy + TLS)
  timothykim.net/    # timothykim.net static site
scripts/
  deploy-dispatch.sh # SSH command dispatcher (validates and routes deploy commands)
  deploy.sh          # Generic deploy script (git pull + docker compose up)
  new-app.sh         # Scaffolding script to add a new app
setup.sh             # One-time server provisioning script
.gitea/workflows/    # Per-app deploy workflows triggered by path changes

Initial server setup

On a fresh Rocky Linux 9 (or compatible) install:

dnf install -y git
git clone https://git.timothykim.net/timothykim/hantim-server.git /opt/hantim
bash /opt/hantim/setup.sh

setup.sh handles everything else: installs git-crypt and the Bitwarden CLI, fetches the git-crypt key from your Bitwarden vault (stored as a base64-encoded secure note named hantim-git-crypt-key), unlocks the repo, installs Docker, creates the deploy user, and starts all services.

To save the git-crypt key to Bitwarden (one-time, from your dev machine):

base64 /path/to/git-crypt-key
# Save the output as a Bitwarden secure note named "hantim-git-crypt-key"

How deploys work

Each app has two components:

  1. Gitea Actions workflow (.gitea/workflows/deploy-<app>.yml) -- triggers on pushes to main when files under docker/<app>/ change. SSHes into the server as the deploy user to trigger the deploy.
  2. SSH dispatcher (scripts/deploy-dispatch.sh) -- the deploy user's authorized_keys is locked to this script via a command= directive. It validates the command and runs scripts/deploy.sh, which pulls the latest repo changes and runs docker compose pull && up -d for that app.

This means:

  • Pushing a change to docker/nginx/compose.yml only deploys nginx.
  • Pushing a change to scripts/ or setup.sh does not trigger any deploy.
  • Each app deploys independently.

Adding a new app

Run the scaffolding script:

./scripts/new-app.sh <app-name>

This creates:

  • docker/<app-name>/compose.yml -- a starter compose file on the shared network
  • .gitea/workflows/deploy-<app-name>.yml -- the Gitea Actions workflow

After running the script:

  1. Edit docker/<app-name>/compose.yml to fit your app (image, ports, volumes, environment variables, etc.).
  2. Commit and push to main.
  3. Configure the proxy host in Nginx Proxy Manager to route traffic to the new service.

Secrets

Nginx Proxy Manager certs and Let's Encrypt account keys are encrypted with git-crypt (see .gitattributes). You need the git-crypt key to unlock them.

The following secrets must be configured in the Gitea repo settings for deploys to work:

  • DEPLOY_HOST -- the server's IP or hostname
  • DEPLOY_KEY -- the SSH private key for the deploy user