|
All checks were successful
Deploy Blog / build-and-deploy (push) Successful in 32s
|
||
|---|---|---|
| .forgejo/workflows | ||
| content | ||
| deployment | ||
| .gitignore | ||
| config.toml | ||
| go.mod | ||
| README.md | ||
So You Don't Have To
War stories, lessons learned, and tales from the trenches of software engineering.
Live at: blog.naiv.no
About This Blog
We document the problems we've encountered, the solutions we've implemented, and the wisdom gained along the way—all so you can avoid making the same mistakes.
Topics include:
- DevOps and infrastructure
- Container orchestration
- Legacy system maintenance
- Debugging adventures
- Production incident post-mortems
- Pragmatic solutions over perfect ones
Stack
- Generator: Hugo with PaperMod theme (via Hugo Modules)
- Repository: Self-hosted Forgejo at kode.naiv.no
- CI/CD: Forgejo Actions
- Hosting: forgejo-pages in Podman container
- Reverse Proxy: Caddy with automatic HTTPS
- Domain: blog.naiv.no
Quick Start
Writing a New Post
# Create new post
hugo new content/posts/my-post-title.md
# Edit the file
vim content/posts/my-post-title.md
# Preview locally
hugo server -D
# When ready, set draft: false and push
git add content/posts/my-post-title.md
git commit -m "Add post: My Post Title"
git push
The blog automatically builds and deploys via Forgejo Actions.
Post Frontmatter
---
title: "Your Post Title"
date: 2025-01-25
draft: false
tags: ["tag1", "tag2"]
categories: ["category"]
summary: "Brief description for listings and SEO"
ShowToc: true
---
Repository Structure
.
├── config.toml # Hugo configuration
├── go.mod # Hugo Modules config
├── content/
│ ├── about.md
│ └── posts/ # Blog posts here
│ ├── container-init-lessons.md
│ └── deploying-this-blog.md
├── static/ # Images, files
├── .forgejo/
│ └── workflows/
│ └── deploy.yml # CI/CD pipeline
└── deployment/ # Deployment configs
├── RUNNER_SETUP.md # Forgejo runner installation
├── podman/
│ ├── podman-compose.yml
│ ├── forgejo-pages.env
│ └── forgejo-pages.service
└── caddy/
├── Caddyfile.snippet # Reverse proxy (forgejo-pages)
├── Caddyfile-direct.snippet # Direct serving
├── sync-blog.sh # Git sync script
├── blog-sync.service # Systemd service
└── blog-sync.timer # Systemd timer
Deployment
Prerequisites
⚠️ Forgejo Actions Runner Required
This blog uses Forgejo Actions for CI/CD. You need to set up a runner first:
📖 See: deployment/RUNNER_SETUP.md for complete runner installation guide.
Quick runner setup:
# Download latest forgejo-runner with GPG verification
cd ~/forgejo-runner
export RUNNER_VERSION=$(curl -s -X 'GET' https://data.forgejo.org/api/v1/repos/forgejo/runner/releases/latest | jq .name -r | cut -c 2-)
wget -O forgejo-runner https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-amd64
wget -O forgejo-runner.asc https://code.forgejo.org/forgejo/runner/releases/download/v${RUNNER_VERSION}/forgejo-runner-${RUNNER_VERSION}-linux-amd64.asc
gpg --keyserver hkps://keys.openpgp.org --recv EB114F5E6C0DC2BCDD183550A4B61A2DC5923710
gpg --verify forgejo-runner.asc forgejo-runner
chmod +x forgejo-runner
# Register with Forgejo (get token from Forgejo UI)
./forgejo-runner register --instance https://kode.naiv.no --token YOUR_TOKEN
# Install systemd service (see RUNNER_SETUP.md for service file)
systemctl --user enable --now forgejo-runner.service
Full Deployment Guide
See the blog post: How This Blog Is Deployed
This blog can be deployed two ways:
- forgejo-pages container (webhook-based, instant updates)
- Caddy serving directly (simpler, timer-based updates)
Both are documented in the blog post. Quick references below.
Quick Deployment Reference
1. Set up forgejo-pages container:
mkdir -p ~/forgejo-pages
cd ~/forgejo-pages
# Copy configs from this repo
cp deployment/podman/* .
# Edit forgejo-pages.env with your API token
# Create data directory
mkdir -p data
# Start with podman-compose
podman-compose up -d
# Set up systemd service
mkdir -p ~/.config/systemd/user
cp forgejo-pages.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now forgejo-pages.service
loginctl enable-linger $USER
2. Configure Caddy:
# Add to your Caddyfile
cat deployment/caddy/Caddyfile.snippet | sudo tee -a /etc/caddy/Caddyfile
# Or use import pattern
sudo cp deployment/caddy/Caddyfile.snippet /etc/caddy/sites/blog.naiv.no
# Reload
sudo systemctl reload caddy
3. Set up DNS:
Add A record: blog.naiv.no → your server IP
4. Push to Forgejo:
git push origin main
Forgejo Actions builds the site and deploys to the static-pages branch. The forgejo-pages container serves it automatically.
Alternative: Caddy Direct (Simpler Setup)
1. Clone static-pages branch:
mkdir -p ~/www/blog.naiv.no
git clone --single-branch --branch static-pages \
https://kode.naiv.no/olemd/so-you-dont-have-to-blog.git \
~/www/blog.naiv.no
2. Set up auto-sync:
# Copy sync script
mkdir -p ~/bin
cp deployment/caddy/sync-blog.sh ~/bin/
chmod +x ~/bin/sync-blog.sh
# Install systemd service and timer
mkdir -p ~/.config/systemd/user
cp deployment/caddy/blog-sync.service ~/.config/systemd/user/
cp deployment/caddy/blog-sync.timer ~/.config/systemd/user/
# Enable timer
systemctl --user daemon-reload
systemctl --user enable --now blog-sync.timer
3. Configure Caddy:
# Use direct serving config
sudo cp deployment/caddy/Caddyfile-direct.snippet /etc/caddy/sites/blog.naiv.no
# Edit to replace 'youruser' with your username
sudo vim /etc/caddy/sites/blog.naiv.no
# Reload
sudo systemctl reload caddy
4. Push to Forgejo:
git push origin main
Blog updates every 5 minutes automatically via systemd timer.
Local Development
# Install Hugo (if not already installed)
# https://gohugo.io/installation/
# Clone repository
git clone https://kode.naiv.no/olemd/so-you-dont-have-to-blog.git
cd so-you-dont-have-to-blog
# Download theme via Hugo Modules
hugo mod get
# Run development server
hugo server -D
# Visit http://localhost:1313
How It Works
- Write: Create Markdown files in
content/posts/ - Push:
git pushto main branch - Build: Forgejo Actions workflow triggers
- Installs Hugo and Go
- Downloads PaperMod theme via Hugo Modules
- Builds static site
- Pushes to
static-pagesbranch
- Serve: forgejo-pages container detects update
- Pulls
static-pagesbranch - Serves files on localhost:8888
- Pulls
- Proxy: Caddy reverse proxies blog.naiv.no → localhost:8888
- Automatic HTTPS via Let's Encrypt
Troubleshooting
Check Build Status
Visit: https://kode.naiv.no/olemd/so-you-dont-have-to-blog/actions
Check forgejo-pages
systemctl --user status forgejo-pages.service
journalctl --user -u forgejo-pages.service -f
Check Caddy
sudo systemctl status caddy
sudo journalctl -u caddy -f
Force Rebuild
git commit --allow-empty -m "Rebuild"
git push
Contributing
Found a typo? Have a suggestion?
- Fork this repository
- Make your changes
- Submit a pull request
Or open an issue: https://kode.naiv.no/olemd/so-you-dont-have-to-blog/issues
License
Content is licensed under CC BY-SA 4.0.
Code examples and configurations are licensed under MIT.
Contact
- Repository: https://kode.naiv.no/olemd/so-you-dont-have-to-blog
- Blog: https://blog.naiv.no
- Issues: https://kode.naiv.no/olemd/so-you-dont-have-to-blog/issues
"The best code is the code that keeps working, even if it's not perfect."