Render, Heroku, and Railway

Docker is not required on any of these three. Each also has a native runtime path that builds straight from your repo's Go source, no Dockerfile involved. Pick whichever your platform dashboard offers; both end up running the same binary the same way.

Shared setup

  1. Connect this repository. Choose Docker (uses the root Dockerfile) or a native/Go runtime if your platform offers one; see each platform's section below for the exact build/start commands either way.
  2. Set FITGUARD_CONFIG to the complete production YAML, pasted as one multi-line environment variable. See What goes where if you haven't yet. Reference secrets as ${OPENAI_API_KEY} inside it, then add those secrets separately as their own env vars in the platform settings.
  3. Set port: ${PORT} in the YAML and configure the platform health check as /healthz.
  4. Use one instance first. Add Redis before adding replicas.

Render

Docker path: create a Web Service from GitHub, choose Docker, and add environment variables under Advanced. Render forwards traffic to the port in PORT and provides HTTPS on its onrender.com domain.

Native path (no Dockerfile, if your Render plan offers a Go environment): same Web Service flow, but choose Go instead of Docker, and set:

Build Command:  go build -o fitguard ./cmd/fitguard
Start Command:  ./fitguard run

Render compiles the binary itself from this repo's go.mod during the build step. There's no curl/install step to write, the same way pip install -r requirements.txt needs no separate download step for a Python service. Config still comes from FITGUARD_CONFIG, same as the Docker path.

This Build/Start Command pair is for FitGuard's own service: a second, independent Web Service on Render, separate from any app you already have running there. If you already have a service on Render (Python, Node, whatever), leave its Build/Start Command exactly as it is; don't merge the two. Add FitGuard as its own service using this repo, get the URL Render gives it, and set that as FITGUARD_URL on your existing service's environment variables. See using FitGuard alongside an existing Dockerfile for the same idea applied to Docker.

For SQLite history (either path), attach a paid persistent disk under Disks and mount it at /var/data. Set data_dir: /var/data. A disk is single-instance and disables zero-downtime deploys, so do not scale this service horizontally with the disk attached.

Heroku

Heroku dyno filesystems are ephemeral, so do not use Heroku for durable SQLite history. Add Heroku Key-Value Store or another TLS Redis service for shared budget state. The entry-level KVS plan is not persistent, so use a production plan when Redis durability matters.

Heroku's container path (below) is the one documented here in full; a classic Go buildpack path also exists on Heroku if you'd rather not use Docker, but isn't covered step-by-step in this guide.

heroku login
heroku create fitguard-prod --stack container
# config.production.yaml here is just a local file on your machine — name it
# anything, it's never committed (see "What goes where" on the overview page):
heroku config:set -a fitguard-prod \
  FITGUARD_CONFIG="$(cat config.production.yaml)" \
  OPENAI_API_KEY="..."
heroku addons:create heroku-redis:mini -a fitguard-prod
heroku container:login
# Build on Apple Silicon for Heroku's x86_64 container runtime:
docker build --platform linux/amd64 -t fitguard .
heroku container:push web -a fitguard-prod
heroku container:release web -a fitguard-prod

Heroku provides PORT and the add-on provides REDIS_URL. The container image's EXPOSE line is not the port configuration. Check https://<app>.herokuapp.com/healthz.

Railway

Create a service from this GitHub repository. Railway detects the root Dockerfile and uses it automatically; delete or rename it in a throwaway clone first if you'd rather Railway's own Nixpacks builder detect and build the Go project directly (via go.mod) with no Dockerfile at all; both produce the same running binary. Add FITGUARD_CONFIG and secrets under Variables either way. Generate a public domain and set the service health check to /healthz.

Attach a Railway Volume at /var/data and set data_dir: /var/data if SQLite history must survive. Railway volumes cannot be used with replicas. For multiple replicas, use Redis for budget and cache state and treat SQLite dashboards as per-instance.

Data rule: Redis does not store FitGuard's request history. It coordinates budget/cache state; SQLite history needs a persistent disk or an external logging design.