timothykim f1d4508411
Deploy nginx / deploy (push) Failing after 2s
Deploy timothykim.net / deploy (push) Failing after 2s
update container names for nginx and timothykim.net
2026-03-13 17:02:26 -04:00
2026-03-12 21:16:59 -04:00
2026-03-12 21:16:59 -04:00
2026-03-12 21:16:59 -04:00

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
S
Description
No description provided
Readme 500 KiB
Languages
Shell 65.2%
Python 33.5%
Assembly 0.5%
Dockerfile 0.4%
HTML 0.4%