Buffalo is the Go ecosystem's answer to Rails — a batteries-included full-stack framework with a frontend asset pipeline, ORM (Pop), migrations (Soda/Fizz), generators, and a dev server. That convenience comes with a more involved build than a bare Go router, because you have both Go code and a Webpack-based frontend to compile. This guide covers deploying a Buffalo app to production.
What Buffalo bundles
A Buffalo app includes:
- Actions (handlers) and a router.
- Pop — the ORM, configured via
database.yml. - Fizz/Soda migrations for schema management.
- An asset pipeline (Webpack by default) for JS/CSS.
- Plush templates for server-rendered HTML.
The key deployment insight: Buffalo can build a single binary that embeds compiled templates and assets via buffalo build. That binary is what you ship.
Building for production
Locally, buffalo build compiles assets and embeds them into one binary:
buffalo build --static -o bin/appThe --static flag produces a statically-linked binary. But in a container you want this to happen reproducibly during the image build, so let's express it as a Dockerfile.
The multi-stage Dockerfile
Buffalo's official base images simplify this, since they include Node and the Buffalo CLI:
# ---- Build ----
FROM gobuffalo/buffalo:v0.18.14 AS build
WORKDIR /src/app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN buffalo build --static -o /bin/app
# ---- Runtime ----
FROM alpine
RUN apk add --no-cache bash ca-certificates
WORKDIR /bin/
COPY --from=build /bin/app .
COPY --from=build /src/app/migrations ./migrations
ENV GO_ENV=production
EXPOSE 3000
CMD ["/bin/app"]We copy the migrations/ directory into the runtime image so we can run them as a release step. The GO_ENV=production flag switches Buffalo into production behavior.
Database configuration with Pop
Pop reads database.yml, but in production you should drive it from the DATABASE_URL env var. Buffalo/Pop natively honor DATABASE_URL, overriding the production block in database.yml:
# database.yml
production:
dialect: postgres
url: {{ envOr "DATABASE_URL" "" }}With a managed PostgreSQL instance linked, DATABASE_URL is injected and Pop connects directly. No credentials in your repo.
Running migrations
Buffalo uses Soda (the migration tool inside Pop) to apply Fizz migrations. Run them as a release step before the new version takes traffic:
/bin/app migrateBuffalo's built binary includes the migrate subcommand, so you don't need a separate tool. On PandaStack, wire /bin/app migrate as a once-per-release job. Avoid running migrations on every pod boot — with multiple replicas you'd race concurrent migrations.
The PORT and binding
Buffalo reads the PORT and ADDR env vars. In a container, set ADDR=0.0.0.0 so it binds to all interfaces and PORT to whatever the platform injects:
ADDR=0.0.0.0
PORT=3000 # or the injected valueIf you forget ADDR=0.0.0.0, Buffalo may bind to 127.0.0.1 and the platform's health checks won't reach it.
Session secret
Buffalo signs session cookies with SESSION_SECRET. Set a stable, random value as a secret env var:
SESSION_SECRET=$(openssl rand -hex 32)Generate once and keep it stable; rotating it logs everyone out on each deploy.
Environment variables
| Variable | Purpose |
|---|---|
GO_ENV | production |
DATABASE_URL | injected by managed DB link |
ADDR | 0.0.0.0 |
PORT | injected listen port |
SESSION_SECRET | cookie signing key |
Asset serving
Because buffalo build embeds compiled assets into the binary (when built with packing), the single binary serves your CSS/JS. For high-traffic sites you might front static assets with a CDN, but the embedded approach keeps deployment to a single artifact — exactly Buffalo's design goal.
Health checks
Add a simple action returning 200 and route it at /health, then point the platform's readiness probe at it. Keep it free of DB access for the liveness probe.
Deploying
Push the repo, connect it, link a managed PostgreSQL database, set the env vars above, and add the /bin/app migrate release step. The platform builds the multi-stage Dockerfile (asset pipeline included) and rolls it out. Live build logs surface Webpack or Go compile errors immediately.
git push origin mainWhen Buffalo fits
Buffalo suits teams who want Rails-like productivity in Go: server-rendered apps with forms, sessions, and a built-in asset pipeline. For pure JSON APIs, a lighter router like Chi or Echo is leaner. Buffalo's value is the integrated full-stack experience.
Conclusion
Buffalo deploys as a single self-contained binary once you handle the dual Go+frontend build, drive Pop from the injected DATABASE_URL, run migrate as a release step, set ADDR=0.0.0.0 and a stable SESSION_SECRET. The integrated tooling pays off for full-stack apps.
Try a Buffalo app with a managed PostgreSQL on PandaStack's free tier — connect your repo at [dashboard.pandastack.io](https://dashboard.pandastack.io) and the database auto-wires via DATABASE_URL.
References
- [Buffalo Documentation](https://gobuffalo.io/documentation/)
- [Buffalo: Deploying to Production](https://gobuffalo.io/documentation/deploy/)
- [Pop / Soda (ORM and Migrations)](https://gobuffalo.io/documentation/database/pop/)
- [Fizz Migration DSL](https://gobuffalo.io/documentation/database/fizz/)
- [Buffalo Docker Images](https://hub.docker.com/r/gobuffalo/buffalo)