replace NPM with vanilla nginx + certbot
- nginx:alpine replaces nginx-proxy-manager - certbot on host with standalone issuance and auto-renewal - per-site nginx configs in conf.d/ (HTTP->HTTPS, bare->www) - container names use underscores (timothykim_net) - setup.sh: automated cert issuance, dynamic app startup - deploy.sh: nginx -t guard, graceful image-not-found - new-app.sh: Gitea API repo creation, nginx conf generation - add ARCHITECTURE.md, USECASES.md, CLAUDE.md - remove old NPM data/certs from tracking
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
# Architecture
|
||||
|
||||
## Servers
|
||||
|
||||
### Hantim server (hantim)
|
||||
|
||||
The production application server. Runs Rocky Linux 9 on Vultr.
|
||||
|
||||
- Hosts all app containers and the nginx reverse proxy via Docker Compose
|
||||
- Provisioned by `setup.sh` in this repo
|
||||
- This repo is cloned to `/opt/hantim`
|
||||
- The `deploy` user receives SSH commands from CI to trigger deploys
|
||||
- Certbot runs on the host (not in Docker) and manages Let's Encrypt certs
|
||||
|
||||
### Gitea server (git.timothykim.net)
|
||||
|
||||
A separate server running the Gitea instance. Provides:
|
||||
|
||||
- Git hosting for all repos
|
||||
- Docker container registry (`git.timothykim.net/hantim/<app>`)
|
||||
- Gitea Actions CI/CD (runner registered at site level)
|
||||
- Organization-level secrets and variables shared across repos
|
||||
|
||||
### Bitwarden (cloud)
|
||||
|
||||
Bitwarden free tier is used as the secret store. Stores:
|
||||
|
||||
- `hantim-git-crypt-key` -- git-crypt symmetric key (Secure Note, base64)
|
||||
- `hantim-ci-registry-push` -- Gitea API token for registry access (Secure Note)
|
||||
- `hantim-server-deploy` -- deploy user SSH key pair (SSH Key)
|
||||
- `hantim-new-app-script` -- Gitea API token for creating repos (Secure Note)
|
||||
|
||||
The Bitwarden CLI (`bw`) is used in `setup.sh` (on the server) and
|
||||
`new-app.sh` (on dev machines) to fetch secrets at runtime.
|
||||
|
||||
## Repos
|
||||
|
||||
All repos live under the `hantim` organization on Gitea.
|
||||
|
||||
### hantim-server
|
||||
|
||||
This repo. Contains server provisioning, Docker Compose configs, nginx
|
||||
configs, deploy scripts, and Gitea Actions workflows.
|
||||
|
||||
```
|
||||
setup.sh # Server provisioning (idempotent)
|
||||
scripts/deploy.sh # Receives deploy commands via SSH
|
||||
scripts/new-app.sh # Scaffolds a new app (Gitea repo + local files)
|
||||
docker/nginx/ # nginx reverse proxy
|
||||
compose.yml # nginx:alpine container
|
||||
nginx.conf # Main nginx config
|
||||
conf.d/<domain>.conf # Per-app server blocks
|
||||
docker/<domain>/compose.yml # Per-app compose files
|
||||
.gitea/workflows/deploy-<app>.yml # Per-app deploy workflows
|
||||
```
|
||||
|
||||
### static-site-template
|
||||
|
||||
A Gitea template repo used by `new-app.sh` to scaffold new static sites.
|
||||
Contains a Dockerfile, nginx config, placeholder HTML, and a `build.yml`
|
||||
workflow that builds/pushes a Docker image and triggers a deploy.
|
||||
|
||||
Uses Gitea template variables (`${REPO_NAME}`) so the image tag and deploy
|
||||
command are automatically set to the repo name.
|
||||
|
||||
### App repos (e.g., timothykim.net)
|
||||
|
||||
Each app is its own repo created from `static-site-template`. Contains the
|
||||
app source code and a `build.yml` workflow.
|
||||
|
||||
## Network topology
|
||||
|
||||
```
|
||||
Internet
|
||||
|
|
||||
[Hantim Server]
|
||||
|
|
||||
nginx (port 80/443)
|
||||
| |
|
||||
timothykim_net hcsuzuki_net ...
|
||||
(shared Docker network)
|
||||
```
|
||||
|
||||
- nginx is the only container with host port bindings (80, 443)
|
||||
- All app containers are on the `shared` Docker network
|
||||
- nginx proxies `https://www.<domain>` to the app's container by name
|
||||
- The `shared` network is created by the nginx compose file and referenced
|
||||
as `external: true` by app compose files
|
||||
|
||||
## Deploy flow
|
||||
|
||||
### App code change (push to app repo)
|
||||
|
||||
```
|
||||
Developer pushes to app repo (e.g., timothykim.net)
|
||||
-> build.yml workflow runs on Gitea runner
|
||||
-> docker build + push to git.timothykim.net registry
|
||||
-> SSH to deploy@hantim as deploy-<domain>
|
||||
-> deploy.sh: git pull, docker compose pull, docker compose up -d
|
||||
-> (if image not yet in registry, skips gracefully)
|
||||
```
|
||||
|
||||
### Infrastructure change (push to hantim-server)
|
||||
|
||||
```
|
||||
Developer pushes to hantim-server
|
||||
-> deploy-<app>.yml triggers (based on changed paths under docker/<app>/)
|
||||
-> SSH to deploy@hantim as deploy-<app>
|
||||
-> deploy.sh: git pull, docker compose pull, docker compose up -d
|
||||
-> (nginx only): test config with nginx -t before applying
|
||||
```
|
||||
|
||||
## Conventions
|
||||
|
||||
- **App name = domain** (e.g., `hcsuzuki.net`)
|
||||
- **Container name** = domain with dots replaced by underscores (e.g.,
|
||||
`hcsuzuki_net`)
|
||||
- **Cert name** = domain (stored at `/etc/letsencrypt/live/<domain>/`)
|
||||
- **Image name** = `git.timothykim.net/hantim/<domain>:latest`
|
||||
- All HTTP traffic redirects to `https://www.<domain>`
|
||||
- Bare domain HTTPS redirects to `https://www.<domain>`
|
||||
Reference in New Issue
Block a user