Skip to content

Commit 63be282

Browse files
authored
Merge pull request #10 from gopherguides/feat/blog-deploying-docker
feat: add Docker deployment tutorial and repo README
2 parents 5ed81db + bbcdbde commit 63be282

2 files changed

Lines changed: 232 additions & 0 deletions

File tree

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# hypemd.dev
2+
3+
The official website and blog for [Hype](https://github.com/gopherguides/hype) — a dynamic Markdown engine that executes code, includes files, and validates content at build time.
4+
5+
**Live site:** [hypemd.dev](https://hypemd.dev)
6+
7+
## What's Here
8+
9+
- **Blog articles** — tutorials and guides in `content/`
10+
- **Documentation** — synced automatically from the [hype repo](https://github.com/gopherguides/hype) via a GitHub Actions workflow
11+
- **Site layouts** — templates in `layouts/`
12+
13+
## Development
14+
15+
The site is built with Hype's built-in blog generator.
16+
17+
```bash
18+
# Install hype
19+
brew install gopherguides/tap/hype
20+
21+
# Build the site
22+
hype blog build
23+
24+
# Serve locally with live reload
25+
hype blog serve
26+
```
27+
28+
## Deployment
29+
30+
The site is deployed via [Dokploy](https://dokploy.com) with autodeploy on push to main. The Dockerfile handles the full build-and-serve pipeline.
31+
32+
## Doc Sync
33+
34+
Documentation pages (`content/docs-*/`) are automatically synced from the hype repo. When docs change in `gopherguides/hype`, a workflow processes them and pushes updates here. Do not edit `content/docs-*` files directly — they will be overwritten on the next sync.
35+
36+
Manually-maintained content:
37+
- `content/docs/module.md` — docs landing page
38+
- `content/getting-started/module.md` — getting started tutorial
39+
- All other `content/*/module.md` — blog articles
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Deploying a Hype Blog with Docker
2+
3+
<details>
4+
slug: deploying-with-docker
5+
published: 03/15/2026
6+
author: Cory LaNou
7+
seo_description: Deploy a Hype-powered blog site with Docker. Covers Dockerfile setup, Dokploy, Heroku, and generic VPS deployment with Docker Compose.
8+
tags: tutorial, docker, deployment, blog, hype
9+
</details>
10+
11+
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.
12+
13+
This is exactly how [hypemd.dev](https://hypemd.dev) runs in production. Here's how to do it.
14+
15+
## The Dockerfile
16+
17+
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:
18+
19+
```dockerfile
20+
FROM golang:1.25 AS builder
21+
RUN go install github.com/gopherguides/hype/cmd/hype@latest
22+
23+
FROM golang:1.25
24+
COPY --from=builder /go/bin/hype /usr/local/bin/hype
25+
WORKDIR /site
26+
COPY . .
27+
RUN hype blog build
28+
EXPOSE 3000
29+
CMD ["hype", "blog", "serve", "--addr", ":3000"]
30+
```
31+
32+
What this does:
33+
34+
1. **Builder stage** — installs `hype` from source using Go 1.25 (hype's minimum version)
35+
2. **Runtime stage** — copies the built binary, copies your site content, runs `hype blog build` to generate the static site, then serves it on port 3000
36+
37+
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.
38+
39+
## Production Serving
40+
41+
As of the latest release, `hype blog serve` supports a `--production` flag that enables embedded Caddy for production-grade serving:
42+
43+
```dockerfile
44+
CMD ["hype", "blog", "serve", "--addr", ":3000", "--production"]
45+
```
46+
47+
This gives you:
48+
49+
- **Compression** — gzip and zstd with automatic content negotiation
50+
- **Security headers** — X-Content-Type-Options, X-Frame-Options, Referrer-Policy
51+
- **Cache control** — 1-year immutable caching for static assets, 1-hour for HTML
52+
- **Clean URLs** — automatic index.html resolution
53+
- **Custom 404** — auto-detected from `public/404.html` if present
54+
55+
No nginx or Caddy sidecar needed. It's all in the binary.
56+
57+
## Deploying with Dokploy
58+
59+
[Dokploy](https://dokploy.com) is a self-hosted PaaS that makes Docker deployments simple. This is what hypemd.dev uses.
60+
61+
### Setup
62+
63+
1. Create a new application in Dokploy and link your GitHub repo
64+
2. Set the build type to **Dockerfile** (not Nixpacks — Dokploy defaults to Nixpacks which won't know how to build a Hype site)
65+
3. Deploy
66+
67+
### Domain Configuration
68+
69+
In Dokploy's domain settings:
70+
71+
- **Host**: your domain (e.g., `hypemd.dev`)
72+
- **Container Port**: `3000`
73+
- **HTTPS**: enable with Let's Encrypt for automatic TLS
74+
75+
Point your DNS A record to your Dokploy server's IP address.
76+
77+
### Auto-Deploy
78+
79+
Enable autodeploy in Dokploy's Git settings. Every push to main triggers a rebuild and redeploy. Combined with the [docs sync workflow](/single-source-docs/) pattern, this means documentation changes in your source repo automatically propagate to your live site.
80+
81+
## Deploying with Heroku
82+
83+
Heroku's container stack works with the same Dockerfile, with one adjustment — Heroku assigns a dynamic port via the `$PORT` environment variable.
84+
85+
### heroku.yml
86+
87+
Create a `heroku.yml` at the repo root:
88+
89+
```yaml
90+
build:
91+
docker:
92+
web: Dockerfile
93+
```
94+
95+
### Dynamic Port
96+
97+
Modify the CMD to use Heroku's port:
98+
99+
```dockerfile
100+
CMD hype blog serve --addr ":$PORT" --production
101+
```
102+
103+
Note the shell form (no brackets) so `$PORT` gets expanded.
104+
105+
### Deploy
106+
107+
```bash
108+
heroku stack:set container
109+
git push heroku main
110+
```
111+
112+
## Generic Docker / VPS Deployment
113+
114+
For any server with Docker installed:
115+
116+
### Build and Run
117+
118+
```bash
119+
docker build -t my-blog .
120+
docker run -d -p 3000:3000 --name my-blog my-blog
121+
```
122+
123+
Your site is now serving on port 3000.
124+
125+
### Docker Compose
126+
127+
For a more complete setup with automatic restarts:
128+
129+
```yaml
130+
services:
131+
blog:
132+
build: .
133+
ports:
134+
- "3000:3000"
135+
restart: unless-stopped
136+
```
137+
138+
```bash
139+
docker compose up -d
140+
```
141+
142+
### TLS with a Reverse Proxy
143+
144+
If you're not using `--production` mode or need TLS termination, put a reverse proxy in front:
145+
146+
```yaml
147+
services:
148+
blog:
149+
build: .
150+
restart: unless-stopped
151+
152+
caddy:
153+
image: caddy:2
154+
ports:
155+
- "80:80"
156+
- "443:443"
157+
volumes:
158+
- ./Caddyfile:/etc/caddy/Caddyfile
159+
- caddy_data:/data
160+
restart: unless-stopped
161+
162+
volumes:
163+
caddy_data:
164+
```
165+
166+
With a `Caddyfile`:
167+
168+
```
169+
yourdomain.com {
170+
reverse_proxy blog:3000
171+
}
172+
```
173+
174+
Caddy handles TLS automatically via Let's Encrypt.
175+
176+
## Content Updates
177+
178+
The deployment workflow is simple:
179+
180+
1. Push content changes to your repo
181+
2. Your platform rebuilds the Docker image
182+
3. `hype blog build` runs inside the container, executing all code blocks fresh
183+
4. The new container starts serving
184+
185+
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.
186+
187+
## Key Takeaways
188+
189+
- **Single binary** — `hype` builds and serves, no external dependencies
190+
- **Docker-native** — simple two-stage Dockerfile works everywhere
191+
- **Production-ready** — `--production` flag adds compression, security headers, and caching
192+
- **Git-driven** — push to deploy, content is always current
193+
- **Build-time validation** — broken code or missing files fail the build, not the reader

0 commit comments

Comments
 (0)