# Game Builds Service Nginx reverse proxy to Hetzner Object Storage with local caching. Upload builds with `s3cmd`, testers access them at `https://builds.kill.systems//`. ## Setup ### 1. Create a public bucket for builds In Hetzner Console → Object Storage, create a new bucket (e.g. `kill-builds`). Set visibility to **public** so Nginx can proxy to it without authentication. Generate S3 credentials if you haven't already (Security → S3 Credentials). ### 2. Configure s3cmd locally ```bash # Install brew install s3cmd # or apt install s3cmd # Configure s3cmd --configure \ --host=fsn1.your-objectstorage.com \ --host-bucket='%(bucket)s.fsn1.your-objectstorage.com' ``` Enter your Hetzner S3 access key and secret key when prompted. ### 3. Push this repo to Gitea ```bash cd game-builds git init git add . git commit -m "game builds proxy" git remote add origin https://src.kill.systems//game-builds.git git push -u origin main ``` ### 4. Deploy in Dokploy 1. Create a new project (e.g. "builds") 2. Add a new **Application** service (not Docker Compose) 3. Set source to your Gitea repo 4. In **Environment Variables**, add: ``` BUCKET_ORIGIN=https://kill-builds.fsn1.your-objectstorage.com/builds/ BUCKET_HOST=kill-builds.fsn1.your-objectstorage.com ``` Replace `kill-builds` with your actual bucket name. 5. Set the port to `3000` 6. Deploy ### 5. Add the domain In the Domains tab, add `builds.kill.systems` with HTTPS and Let's Encrypt on port 3000. DNS at Squarespace: ``` Host: builds Type: A Data: 46.224.133.129 ``` ## Uploading Builds Edit `deploy.sh` and set your bucket name, then: ```bash # Upload current build (uses git hash as version) ./deploy.sh ./dist # Upload with a specific version name ./deploy.sh ./dist v1.2.3 # Upload with a custom label ./deploy.sh ./dist beta-march15 ``` Testers visit `https://builds.kill.systems//` to play. ## How it works ``` Tester → builds.kill.systems → Nginx (cache) → Hetzner Object Storage ↓ Cached for 30 days (builds are immutable) ``` First request fetches from Object Storage and caches locally. Subsequent requests are served from the Nginx cache. The `X-Cache-Status` response header shows `HIT` or `MISS`. Since each build has a unique path (the git hash), cache invalidation is never needed — new builds go to new paths.