Compare commits

...

10 Commits

Author SHA1 Message Date
kennethreitz 70a03d03b0 Scrub access key IDs before publishing
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:37:33 -04:00
kennethreitz 632d4f93f4 Gitea: custom homepage with featured repos
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:36:36 -04:00
kennethreitz 02376ccc87 Gitea: kennethreitz-archive org is public
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:33:41 -04:00
kennethreitz 2bf5de4de8 Gitea: mirror not-kennethreitz and kennethreitz-archive orgs too
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:32:39 -04:00
kennethreitz 0f2da47321 Split MinIO into media + infra-backup instances; add Gitea
- photos minio -> media.kennethreitz.org (media-minio console)
- new infra project: minio-backups on s3.kennethreitz.org (Dokploy
  backup destination), gitea on git.kennethreitz.org mirroring all
  github.com/kennethreitz repos every 8h
- note the compose-domains-are-deploy-time-labels gotcha

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:29:07 -04:00
kennethreitz a58cd5ac78 minio: moved under the photos project
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:18:30 -04:00
kennethreitz 726014f1bb Document mercury-objects volume
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:13:32 -04:00
kennethreitz a40fc72d3c Add minio deployment and backup destination to inventory
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 02:08:43 -04:00
kennethreitz 7b2b72dc31 Replace ACME note: HTTP-01 in effect, document rate-limit lessons
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 01:27:50 -04:00
kennethreitz 26309782a1 Document photos compose deployment and DNS-01 ACME switch
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 01:04:34 -04:00
2 changed files with 110 additions and 0 deletions
+89
View File
@@ -144,3 +144,92 @@ Note: repo is heavy (~1.4 GB .git, mp3s tracked directly) — clones/builds are
but the site rarely changes.
Redeploy: `scripts/api.sh application.deploy '{"applicationId":"arptPVNJZU0SIsk7z8G-E"}'`
### photos
> photos.kennethreitz.org (ExifTree) — Django photo portfolio
| | |
|---|---|
| Project ID | `ZTrEB4hHFibiOjqGUaEvF` |
| Environment | `production` (`p0XGhTIy3NvO3Flwr7bVU`) |
#### Compose: photos (web + worker)
| | |
|---|---|
| URL | https://photos.kennethreitz.org |
| Compose ID | `WICLbVwy5JEbHz2SPb4tR` (appName `exiftree-1fms6x`) |
| Source | Custom git `git@github.com:kennethreitz/photos.kennethreitz.org.git`, branch `main`, `compose.prod.yml` |
| Auth | Dokploy SSH key `github-photos` (`a1BX7DYoCglQFXAwTEmy0`), read-only deploy key |
| Services | `web` (runbolt :8000, Traefik routes here) + `worker` (Celery, concurrency 2) |
| Domain | `photos.kennethreitz.org` (`x9rE8KS2nDGXlQjpUorfm`, serviceName `web`) — covered by wildcard `*.kennethreitz.org` A record |
| Env | DEBUG/SECRET_KEY/ALLOWED_HOSTS/DATABASE_URL/CELERY_*/AWS_* in Dokploy compose env. Celery brokers through Postgres via `sqla+postgresql+psycopg://` (explicit env override — repo only ships psycopg v3, default `sqla+postgresql://` wants psycopg2). `OPENAI_API_KEY` empty for now (AI describe disabled). |
| Storage | Tigris bucket `exiftree-media` (kept from Fly era; creds in compose env — rotate if needed) |
| Auto-deploy | GitHub webhook (id `636448741`) → `/api/deploy/compose/<refreshToken>` |
| Deployed | 2026-06-05 (migrated from Fly.io app `exiftree`; old `deploy.yml` + `daily-restart.yml` workflows removed) |
#### Database: exiftree-db
| | |
|---|---|
| Postgres ID | `BgAzfQ5lAXvd6jORAz2bs` (appName/host `exiftree-db-fsgd0w`) |
| Image | `postgres:17` (matches the Fly source version — pg16 cannot read pg17 dumps) |
| DB/user | `exiftree` / `exiftree` (password in Dokploy) |
| Data | Restored 2026-06-05 from Fly Postgres `exiftree-db` dump — 20,301 images, 168 MB. The Fly DB app still exists as a fallback snapshot. |
Redeploy: `scripts/api.sh compose.deploy '{"composeId":"WICLbVwy5JEbHz2SPb4tR"}'`
#### Compose: minio (photos media store)
> MinIO for photo media. Lives in the photos project. A *separate* MinIO in the
> infra project handles backups — see below.
| | |
|---|---|
| Compose ID | `UK8pWczw8d9GSmyLjZJiP` (appName `minio-q3xgqx`, sourceType raw, env `production` of photos) |
| S3 API | https://media.kennethreitz.org (container port 9000) |
| Console | https://media-minio.kennethreitz.org (container port 9001) |
| Data dir | `/mnt/objects/minio` — bind mount on the 250 GB `mercury-objects` Hetzner volume |
| Root user | `exiftree-admin` (password in Dokploy compose env) |
| Buckets | `exiftree-media` (anonymous download enabled) |
| Service account | access key in Dokploy (photos compose env) |
⚠️ Gotcha learned the hard way: Dokploy compose domains are applied as **container
labels at deploy time** — `domain.create`/`delete` alone changes nothing until the
compose is redeployed. Stale labels keep routing the old hostname.
### infra
> Infrastructure services (own project)
#### Compose: minio-backups
| | |
|---|---|
| Compose ID | `o5LlW9tAugh9K3nf5CTh5` (appName `minio-backups-o5fjyf`, raw) |
| S3 API | https://s3.kennethreitz.org (port 9000) |
| Console | https://minio.kennethreitz.org (port 9001) |
| Data dir | `/mnt/objects/minio-infra` (on the volume) |
| Root user | `infra-admin` (password in Dokploy compose env) |
| Buckets | `dokploy-backups` (private) |
| Service account | access key in the Dokploy destination config |
#### Compose: gitea
| | |
|---|---|
| Compose ID | `PV7bUFe4wV-2G1WD8H57e` (appName `gitea-qdogok`, raw) |
| URL | https://git.kennethreitz.org (container port 3000) |
| Git SSH | `git.kennethreitz.org:2222` (host port 2222 → container 22) |
| Data dir | `/var/lib/gitea` (root disk; sqlite DB) |
| Admin | `kennethreitz` (password noted at setup; registration disabled) |
| Homepage | Custom landing page (`/var/lib/gitea/gitea/templates/home.tmpl` on the host) with a "Featured projects" card grid — edit the file and `docker restart` the gitea container to change picks |
| Mirrors | Pull-mirrors (8h refresh) of all repos from github.com/**kennethreitz** (99), **not-kennethreitz** (38), and **kennethreitz-archive** (154) — all three owners public on Gitea — created via `/api/v1/repos/migrate` with a GitHub token |
### Backups
| | |
|---|---|
| Destination | `minio` (`gZBEIrDnfDmRcv_pQQO7q`) → bucket `dokploy-backups` via https://s3.kennethreitz.org |
| Schedule | exiftree-db (postgres): daily 06:00 UTC, keep 14 (`backupId Wa4DDnhr2alAK_f7o7C5I`) — verified working with a manual run |
+21
View File
@@ -18,9 +18,11 @@
|---|---|
| OS | Ubuntu 26.04 LTS |
| Kernel | 7.0.0-15-generic |
| Server type | Hetzner Cloud CPX31 (id `136742397`, dc `ash-dc1`) |
| CPU | 4 vCPU |
| RAM | 7.6 GiB |
| Disk | 150 GB (`/dev/sda1`) |
| Volume | `mercury-objects` (id `105925944`), 250 GB ext4 at `/mnt/objects` (fstab, nofail) — MinIO data |
## Stack
@@ -46,3 +48,22 @@ See [inventory.md](inventory.md). Currently:
- **kjvstudy** — https://kjvstudy.org (built from `kennethreitz/kjvstudy.org`)
- **kennethreitz.org** — https://kennethreitz.org (built from `kennethreitz/kennethreitz.org`)
- **interpretations** — https://interpretations.kennethreitz.org (built from `kennethreitz/interpretations`)
- **photos** — https://photos.kennethreitz.org (compose: web + celery worker + postgres17 db, from `kennethreitz/photos.kennethreitz.org`)
## TLS / ACME
Traefik's `letsencrypt` resolver uses the **HTTP-01 challenge**. All certs issued.
Lessons from the 2026-06-05 Fly migration (cost ~1.5h of cert warnings):
- While DNS still pointed at Fly, every validation failed; 5 failed
authorizations/hour/domain trips Let's Encrypt's rate limiter, and **each
retry during the stale window extends it** — when this happens, stop
retrying and wait out the window (exact expiry is in the 429 in Traefik logs).
- After a rate-limit stall, Traefik does not retry on its own — restart the
`dokploy-traefik` container to trigger fresh orders.
- A DNS-01 attempt via DNSimple failed because lego requires an **account**
token (`dnsimple_a_…`); user tokens (`dnsimple_u_…`) are rejected with
"user tokens are not supported". With an account token, DNS-01 (set via
`settings.updateTraefikConfig` + `settings.writeTraefikEnv` with
`DNSIMPLE_OAUTH_TOKEN`) is immune to stale-DNS validation failures and
enables wildcard certs.