# 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. timothykim.net.conf) compose.yml timothykim.net/ # timothykim.net static site compose.yml scripts/ deploy.sh # Deploy script (validates command, git pull, docker compose up) new-app.sh # Scaffolding script to add a new app setup.sh # Server provisioning script (idempotent) .gitea/workflows/ # Per-app deploy workflows triggered by path changes ``` ## Provisioning a new server On a fresh Rocky Linux 9 install: ```bash dnf install -y git git clone https://git.timothykim.net/hantim/hantim-server.git /opt/hantim bash /opt/hantim/setup.sh ``` `setup.sh` is fully automated and idempotent. It: 1. Installs system dependencies (git-crypt, jq, certbot, Node.js 20, Docker) 2. Installs and authenticates the Bitwarden CLI 3. Unlocks git-crypt using a key stored in Bitwarden 4. Fetches the Docker registry token and deploy SSH public key from Bitwarden 5. Logs into the Gitea Docker registry 6. Creates the `deploy` user with restricted SSH access and sudo 7. Issues SSL certificates via certbot for all configured domains 8. Starts all services (nginx first, then all apps) **Prerequisites**: DNS for all configured domains must point to the server before running setup. ## Adding a new app ```bash ./scripts/new-app.sh ``` Example: `./scripts/new-app.sh hcsuzuki.net` This creates: - A Gitea repo (`hantim/`) from the `static-site-template` - `docker//compose.yml` -- app compose file - `.gitea/workflows/deploy-.yml` -- deploy workflow - `docker/nginx/conf.d/.conf` -- nginx proxy config (HTTP->HTTPS, bare domain->www) After running the script: 1. Issue the SSL cert on the server (**before** pushing): ```bash docker stop nginx certbot certonly --standalone --non-interactive --agree-tos \ --register-unsafely-without-email --cert-name \ -d -d www. docker start nginx ``` 2. Commit and push this repo to deploy 3. Clone the new app repo, customize it, and push to build/deploy ## Deploying changes **App code change** (push to an app repo like `timothykim.net`): - The app's `build.yml` builds 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//` paths changed - `deploy.sh` pulls the latest code and runs `docker 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. `setup.sh` is idempotent: - Existing packages are skipped - Existing certs are skipped (or re-issued if the server is new) - All services are started ## Secrets The following are stored in Bitwarden and fetched automatically by `setup.sh`: | Bitwarden item | Type | Purpose | |---|---|---| | `hantim-git-crypt-key` | Secure Note | git-crypt symmetric key (base64) | | `hantim-ci-registry-push` | Secure Note | Gitea token for Docker registry | | `hantim-server-deploy` | SSH Key | Deploy user's SSH key | The following are configured in the `hantim` Gitea org settings: | 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 | The following is stored in Bitwarden and used by `new-app.sh`: | Bitwarden item | Type | Purpose | |---|---|---| | `hantim-new-app-script` | Secure Note | Gitea API token for creating repos |