December 20, 2025
Successfully integrated Zine v0.11.2 as the static site generator for the blog infrastructure.
Key Achievements:
- Built Docker container with Zine pre-compiled binary from GitHub releases
- Configured automatic git-based deployment pipeline
- Fixed output directory mapping (
public/ → /output) - Implemented automated build-on-push workflow with 5-minute polling interval
- Added openssh-client to container for git operations
Technical Stack:
- Container: Alpine Linux with Zig 0.15.2 + Zine 0.11.2
- Git repository: Hosted on self-hosted Gitea (gitea.auc.fi)
- Blog content served via nginx through Envoy reverse proxy
- HTTPS with Let’s Encrypt certificates (blog.auc.fi)
- Automated builds triggered by git commits to master branch
Architecture:
Git Push → Gitea → Container Polls (5min) → Zine Build → rsync → nginx → Envoy → HTTPS
December 19, 2025
Resolved multiple issues with the Zig build system for the blog infrastructure. This was a comprehensive debugging session that uncovered several Docker and Zig-specific challenges.
Problems Solved:
- Permission Issues: Container was initially running as root, causing file ownership conflicts. Fixed by setting
user: "1000:100" to match NixOS host user - Zig Cache Errors: Resolved “AccessDenied” errors for
/.cache/zig by adding --global-cache-dir flag pointing to mounted volume - build.zig.zon Syntax: Removed problematic
build.zig.zon file that caused “expected enum literal” errors with Zig 0.15.2 - Volume Mount Caching: Discovered Docker volume mounts can cache stale file content across container restarts
- Git SSH Access: Added openssh-client package to enable git pull operations from Gitea
Lessons Learned:
- Zig 0.15.2 has very strict requirements for build.zig.zon format - anonymous struct syntax
.{ .name = "blog" } doesn’t work - Docker container user IDs must match host user IDs for proper file access in mounted volumes
docker compose restart doesn’t always clear volume mount caches - full stop/start may be needed- For simple use cases, running without build.zig.zon is cleaner than fighting format issues
Architecture Decisions:
- Chose to call
zine release directly rather than wrapping it in Zig build system - Simplified build.zig to minimal stub since Zine handles all build logic
- Used git polling instead of webhooks for simplicity and reliability
December 07, 2025
Added n8n workflow automation service to the infrastructure stack.
Configuration:
- Service: n8n-custom:latest (custom Docker image with docker CLI)
- Domain: n8n.auc.fi through Envoy proxy
- User: 1000:131 (matching host user + docker group)
- Volume: ./data/n8n for persistent workflow data
- Access: Docker socket mounted for container management capabilities
Use Cases:
- Automated SSL certificate renewal scheduling
- Infrastructure monitoring and alerting
- Service health checks
- Future automation workflows
Integration:
- Added n8n virtual host to Envoy configuration
- Configured HTTPS with Let’s Encrypt certificate
- Set up proper webhook URL (https://n8n.auc.fi/)
- Enabled proxy trust for correct IP handling
December 07, 2025
Implemented automated SSL certificate renewal system using Let’s Encrypt and Certbot.
Features:
- Automated renewal for 6 domains: auc.fi, blog.auc.fi, countly.auc.fi, gitea.auc.fi, hsc.auc.fi, n8n.auc.fi
- HTTP-01 challenge validation through Envoy proxy
- SAN (Subject Alternative Name) certificate covering all domains
- Automatic certificate copying to all domain directories
- Automatic Envoy reload after renewal
Architecture:
- certbot-webroot.local: nginx:alpine serving ACME challenges
- Envoy routes
/.well-known/acme-challenge/ to certbot service - certbot-renew.sh script orchestrates renewal process
- Planned: n8n workflow for scheduled weekly renewals
Technical Implementation:
certbot certonly --webroot -w /var/www/certbot \
-d auc.fi -d blog.auc.fi -d countly.auc.fi \
-d gitea.auc.fi -d hsc.auc.fi -d n8n.auc.fi
December 07, 2025
Deployed Headscale as a self-hosted Tailscale coordination server.
Services:
- headscale.local: Coordination server (headscale:stable)
- headscale-ui.local: Web management interface
- Domain: hsc.auc.fi through Envoy
Configuration:
- Data persistence: ./data/headscale/data
- Config: ./data/headscale/config/config.yaml
- Health checks enabled
- HTTPS access via Envoy proxy
Purpose:
- Self-hosted WireGuard mesh VPN
- Secure access to infrastructure services
- Alternative to cloud-hosted Tailscale
December 19, 2025
Created the foundational build configuration for the blog infrastructure.
Repository Structure:
blog/
├── content/ # Markdown content files
│ ├── blog/ # Blog posts
│ └── devlog/ # Development log entries
├── layouts/ # Zine template files
├── assets/ # Static assets (CSS, JS, images)
├── zine.ziggy # Zine configuration
├── build.zig # Zig build script
└── blog-watch.sh # Git polling and build automation
Components:
- blog-watch.sh: Polls git repository every 5 minutes, triggers builds on changes
- build.zig: Minimal build script for Zig integration
- Git repository: Hosted on gitea.auc.fi:AUC/blog.git
- Branch: master (configured via GIT_BRANCH env var)
Automation Flow:
- Developer pushes to blog repository
- Container polls git every 300 seconds
- Detects changes via
git fetch + hash comparison - Pulls latest changes
- Runs
zine release to build static site - Rsyncs output to nginx document root
- Site immediately available via HTTPS
December 01, 2025
Current auc.fi infrastructure stack running on NixOS with Docker Compose:
Core Services:
- Envoy Proxy: HTTPS termination, SNI-based routing, TLS inspection
- nginx: Static content hosting (main site, blog)
- Countly: Analytics platform (API + Frontend + MongoDB)
- Gitea: Self-hosted Git service on port 2222
- Headscale: Self-hosted Tailscale coordination server
- n8n: Workflow automation platform
- Zine Builder: Blog build automation
- Certbot: SSL certificate management
Domains:
- auc.fi - Main site (Nuxt.js static)
- blog.auc.fi - Blog (Zine static site generator)
- countly.auc.fi - Analytics dashboard
- gitea.auc.fi - Git repository hosting
- hsc.auc.fi - Headscale VPN coordination
- n8n.auc.fi - Workflow automation
Network Architecture:
- All HTTPS traffic → Envoy (port 443)
- HTTP traffic → Envoy (port 80) for ACME challenges
- SSH traffic → UniFi Dream Machine Pro → Gitea (port 22 → 2222)
- All services in Docker bridge network with Envoy as gateway