Native Windows deployment: config file, service, signed auto-update
- internal/config: .env-style config file (gpu-turnstile.env next to the exe, -config flag or GPU_TURNSTILE_CONFIG); process env overrides file. - internal/service: Windows service via golang.org/x/sys/windows/svc — graceful SCM stop, 'service install/remove' commands, restart-on-failure recovery (also applies staged updates). First external dependency, Windows-only; Linux/Docker build unaffected (go.mod stays at 1.23). - internal/update: polls the Gitea releases API, verifies the Ed25519 signature of the downloaded binary against an embedded public key (openssl-signed by CI), swaps it in next to the running exe, and once the GPU lock is idle exits with code 3 so service recovery restarts onto the new version. Dev builds and empty pubkey never update. - CI: tag builds additionally produce gpu-turnstile.exe + .sig + .sha256 attached to a Gitea release. - LOG_FILE env var so the service has somewhere to log.
This commit is contained in:
@@ -28,8 +28,11 @@ Open WebUI / n8n ────► :8188 ───┘
|
||||
|
||||
## Configuration
|
||||
|
||||
All configuration is via environment variables; invalid values fail at
|
||||
startup.
|
||||
Configuration comes from environment variables and/or an `.env`-style
|
||||
config file (`KEY=VALUE` lines, `#` comments). File lookup order:
|
||||
`-config <path>` flag, then `GPU_TURNSTILE_CONFIG`, then
|
||||
`gpu-turnstile.env` next to the executable. Process environment variables
|
||||
override file values. Invalid values fail at startup.
|
||||
|
||||
| Var | Default | Meaning |
|
||||
|---|---|---|
|
||||
@@ -43,6 +46,7 @@ startup.
|
||||
| `WARM_MODEL` | _(empty)_ | Model to reload after an image job (off by default) |
|
||||
| `LOGLEVEL` | `warn` | `info` logs every request (colored arrows in text mode), `debug` adds lock transitions. `LOG_LEVEL` works as an alias |
|
||||
| `LOG_FORMAT` | `text` | `json` for structured JSON logs |
|
||||
| `LOG_FILE` | _(empty)_ | Append logs to this file instead of stderr |
|
||||
| `UNLOAD_POLL_INTERVAL` | `500ms` | `/api/ps` poll interval while unloading |
|
||||
| `HISTORY_POLL_INTERVAL` | `1s` | `/history/<id>` poll interval while a job runs |
|
||||
| `PROBE_TIMEOUT` | `5s` | Startup probe of both upstreams |
|
||||
@@ -52,6 +56,10 @@ startup.
|
||||
| `BACKOFF_INITIAL` | `1s` | First retry wait when an upstream refuses a connection |
|
||||
| `BACKOFF_MAX` | `60s` | Cap for the exponential retry backoff |
|
||||
| `PROMPT_CAPTURE_LIMIT` | `65536` | Bytes of the `/prompt` response buffered to find `prompt_id` (pass-through is unaffected) |
|
||||
| `AUTO_UPDATE` | `true` | Poll the Gitea releases API for signed updates |
|
||||
| `UPDATE_INTERVAL` | `6h` | Auto-update check interval |
|
||||
| `UPDATE_REPO` | `https://git.rambossek.at/PUBLIC/gpu-turnstile` | Repository checked for releases |
|
||||
| `UPDATE_ASSET` | `gpu-turnstile.exe` | Release asset to download |
|
||||
|
||||
## Observability
|
||||
|
||||
@@ -74,6 +82,32 @@ go build ./cmd/gpu-turnstile
|
||||
./gpu-turnstile
|
||||
```
|
||||
|
||||
### Run natively on Windows (primary deployment)
|
||||
|
||||
Download `gpu-turnstile.exe` from a release, put a `gpu-turnstile.env`
|
||||
next to it, and run it — or install it as a Windows service from an
|
||||
elevated shell:
|
||||
|
||||
```sh
|
||||
gpu-turnstile.exe service install # auto-start service, recovery = restart
|
||||
gpu-turnstile.exe service remove
|
||||
```
|
||||
|
||||
The service uses the config file (services have no convenient
|
||||
environment); set `LOG_FILE` in it since there is no console.
|
||||
|
||||
**Auto-update is on by default**: the binary checks the repo's latest
|
||||
release on startup and every `UPDATE_INTERVAL`, verifies the Ed25519
|
||||
signature of the download against the public key embedded at build time,
|
||||
and — once the GPU lock is idle — restarts the service onto the new
|
||||
version. Disable with `AUTO_UPDATE=false`. Releases are signed by CI with
|
||||
OpenSSL; the matching public key lives in `internal/update/pubkey.go`
|
||||
(one-time setup: `openssl genpkey -algorithm ed25519 -out private.pem`,
|
||||
`openssl pkey -in private.pem -pubout -out public.pem`; private key goes
|
||||
to the `SIGNING_KEY` repo secret, public key is committed).
|
||||
|
||||
### Docker
|
||||
|
||||
```sh
|
||||
docker build -t gpu-turnstile .
|
||||
docker run --rm -p 11434:11434 -p 8188:8188 \
|
||||
@@ -84,9 +118,10 @@ docker run --rm -p 11434:11434 -p 8188:8188 \
|
||||
|
||||
Releases are built by Gitea Actions (`.gitea/workflows/ci.yml`): every push
|
||||
runs `go vet` and `go test -race`, and pushing a semantic-version tag
|
||||
`vX.Y.Z` builds and publishes
|
||||
`git.rambossek.at/<owner>/gpu-turnstile:vX.Y.Z` (and updates `:latest`).
|
||||
No images are built from branches.
|
||||
`vX.Y.Z` publishes the container image
|
||||
(`git.rambossek.at/<owner>/gpu-turnstile:vX.Y.Z` plus `:latest`) and a
|
||||
signed Windows binary attached to a Gitea release. Nothing is built from
|
||||
branches.
|
||||
|
||||
The registry login needs one repository secret (Settings → Actions →
|
||||
Secrets): `REGISTRY_TOKEN` — an access token with `write:package` scope.
|
||||
@@ -99,13 +134,17 @@ go vet ./...
|
||||
go test -race ./...
|
||||
```
|
||||
|
||||
Stdlib only, Go 1.23+. Layout:
|
||||
Go 1.23+; the only external dependency is `golang.org/x/sys` (Windows
|
||||
service integration, unused in the Linux build). Layout:
|
||||
|
||||
```
|
||||
cmd/gpu-turnstile/main.go wiring, config, listeners
|
||||
cmd/gpu-turnstile/main.go wiring, config, listeners, service + updater
|
||||
internal/lock/ two-mode lock (LLM readers / image writer, FIFO)
|
||||
internal/ollama/ ps / unload / warm client
|
||||
internal/comfy/ history poll / free client
|
||||
internal/proxy/ handlers for both listeners
|
||||
internal/metrics/ Prometheus exposition, no dependencies
|
||||
internal/config/ env + .env file configuration
|
||||
internal/update/ signed auto-updater
|
||||
internal/service/ Windows service integration
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user