The Dockerfile
The following multi-stage build covers the default GRIP application:Dockerfile
copper build embeds it. This ordering matters: copper build embeds whatever is in web/build but does not run the frontend build itself, so the Go image never needs a JavaScript toolchain. If you use npm instead of bun, swap the first stage for a node image and copy package-lock.json in place of bun.lock.
Caching Dependencies
Each stage copies its dependency manifests and installs before copying the rest of the source. Docker caches those layers, so day-to-day builds skipbun install and go mod download entirely unless a lockfile changes. You are free to tighten this further with a .dockerignore:
.dockerignore
Running Migrations
The image includesmigrate.out, so you may run migrations as a release step before starting the new version:
API-Only Applications
If your application was created with-frontend none, drop the first stage and the COPY --from=web line; the rest of the Dockerfile works as-is.
If you use the
sqlite3 storage, the binary depends on cgo. Build with CGO_ENABLED=1 and a C toolchain (apk add gcc musl-dev on alpine), and keep the builder and runtime libc in sync. The Postgres and MySQL drivers are pure Go, so the default stack needs none of this.Graceful Shutdown
Copper shuts down cleanly onSIGTERM, which is what docker stop and most orchestrators send. Since in-flight requests, background goroutines, and cleanup hooks are given up to 30 seconds, you should allow at least that long before the platform escalates to SIGKILL: for example, docker stop --timeout 35 or terminationGracePeriodSeconds: 35 in Kubernetes.