Deploying a Hype Blog with Docker
Hype builds and serves your blog in a single binary. That makes it a natural fit for Docker — one container that builds your site from source and serves it, with no external web server required.
This is exactly how hypemd.dev runs in production. Here's how to do it.
The Dockerfile
A Hype blog needs two things at deploy time: the hype binary and your site content. A two-stage Docker build keeps the image lean:
FROM golang:1.25 AS builder
RUN go install github.com/gopherguides/hype/cmd/hype@latest
FROM golang:1.25
COPY --from=builder /go/bin/hype /usr/local/bin/hype
WORKDIR /site
COPY . .
RUN hype blog build
EXPOSE 3000
CMD ["hype", "blog", "serve", "--addr", ":3000"]
What this does:
- Builder stage — installs
hypefrom source using Go 1.25 (hype's minimum version) - Runtime stage — copies the built binary, copies your site content, runs
hype blog buildto generate the static site, then serves it on port 3000
The hype blog build step executes all your code blocks, resolves includes, and generates public/. The hype blog serve command serves that directory with live reload in development, or you can add the --production flag for production-grade serving with compression and security headers.
Production Serving
As of the latest release, hype blog serve supports a --production flag that enables embedded Caddy for production-grade serving:
CMD ["hype", "blog", "serve", "--addr", ":3000", "--production"]
This gives you:
- Compression — gzip and zstd with automatic content negotiation
- Security headers — X-Content-Type-Options, X-Frame-Options, Referrer-Policy
- Cache control — 1-year immutable caching for static assets, 1-hour for HTML
- Clean URLs — automatic index.html resolution
- Custom 404 — auto-detected from
public/404.htmlif present
No nginx or Caddy sidecar needed. It's all in the binary.
Deploying with Dokploy
Dokploy is a self-hosted PaaS that makes Docker deployments simple. This is what hypemd.dev uses.
Setup
- Create a new application in Dokploy and link your GitHub repo
- Set the build type to Dockerfile (not Nixpacks — Dokploy defaults to Nixpacks which won't know how to build a Hype site)
- Deploy
Domain Configuration
In Dokploy's domain settings:
- Host: your domain (e.g.,
hypemd.dev) - Container Port:
3000 - HTTPS: enable with Let's Encrypt for automatic TLS
Point your DNS A record to your Dokploy server's IP address.
Auto-Deploy
Enable autodeploy in Dokploy's Git settings. Every push to main triggers a rebuild and redeploy. Combined with the docs sync workflow pattern, this means documentation changes in your source repo automatically propagate to your live site.
Deploying with Heroku
Heroku's container stack works with the same Dockerfile, with one adjustment — Heroku assigns a dynamic port via the $PORT environment variable.
heroku.yml
Create a heroku.yml at the repo root:
build:
docker:
web: Dockerfile
Dynamic Port
Modify the CMD to use Heroku's port:
CMD hype blog serve --addr ":$PORT" --production
Note the shell form (no brackets) so $PORT gets expanded.
Deploy
heroku stack:set container
git push heroku main
Generic Docker / VPS Deployment
For any server with Docker installed:
Build and Run
docker build -t my-blog .
docker run -d -p 3000:3000 --name my-blog my-blog
Your site is now serving on port 3000.
Docker Compose
For a more complete setup with automatic restarts:
services:
blog:
build: .
ports:
- "3000:3000"
restart: unless-stopped
docker compose up -d
TLS with a Reverse Proxy
If you're not using --production mode or need TLS termination, put a reverse proxy in front:
services:
blog:
build: .
restart: unless-stopped
caddy:
image: caddy:2
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
restart: unless-stopped
volumes:
caddy_data:
With a Caddyfile:
yourdomain.com {
reverse_proxy blog:3000
}
Caddy handles TLS automatically via Let's Encrypt.
Content Updates
The deployment workflow is simple:
- Push content changes to your repo
- Your platform rebuilds the Docker image
hype blog buildruns inside the container, executing all code blocks fresh- The new container starts serving
Every deploy is a clean build. Your code examples are re-executed, includes are re-resolved, and broken references fail the build before they reach production.
Key Takeaways
- Single binary —
hypebuilds and serves, no external dependencies - Docker-native — simple two-stage Dockerfile works everywhere
- Production-ready —
--productionflag adds compression, security headers, and caching - Git-driven — push to deploy, content is always current
- Build-time validation — broken code or missing files fail the build, not the reader