Host Your Own Website with Hugo - Like Serverküche Itself
DEV Community

Host Your Own Website with Hugo - Like Serverküche Itself

The page you're reading right now runs on exactly the setup from this tutorial: Hugo builds static HTML from Markdown files, and a tiny nginx container serves it behind Traefik. No WordPress, no database, no PHP security updates - and still a fully-featured website. This is maximum dogfooding: you're reading a recipe on its own result. What are we building? Your own website with the static site generator Hugo (v0.165.0), built in a multi-stage Docker image and served by nginx 1.31 behind Traefik with automatic HTTPS. By the end you'll have a blog-capable site under your domain, generated from simple Markdown files - and you'll know how to publish new posts with a single rebuild. For design we use the popular PaperMod theme; the principle applies to any Hugo theme. The big advantage over a classic CMS like WordPress: there's no attack surface at runtime. What's served is plain HTML, and the container contains no interpreter and no database that could be compromised. Prerequisites - A server with Traefik running as a reverse proxy (provides the HTTPS certificate) - Docker & Docker Compose installed - A domain pointing at the server - replace YOUR_DOMAIN below - git on the server (apt install git ) to fetch the theme Step by step Step 1: Create the Hugo project Create the project folder. Everything that makes up the site lives in this directory - including the Docker files: mkdir -p /opt/hugo-demo/content/posts && cd /opt/hugo-demo Get the PaperMod theme. We clone it as a regular folder (not a Git submodule) so it's guaranteed to be in the Docker build context: git clone --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod rm -rf themes/PaperMod/.git --depth=1 fetches only the current state (no history), and deleting .git afterwards makes the theme a fixed part of your project. Step 2: Configure Hugo The central config is hugo.toml . Replace YOUR_DOMAIN with your real domain - Hugo builds absolute links on top of it: baseURL = "https://YOUR_DOMAIN/" languageCode = "en-us" title = "My Serverküche Site" theme = "PaperMod" [params] description = "A test blog, hosted with Hugo and Docker." [[menu.main]] name = "Posts" url = "/posts/" weight = 1 baseURL is crucial: if it holds the wrong domain, all internal links and assets point nowhere. The menu links to the post overview that Hugo generates automatically from the content/posts/ folder. Step 3: Write content as Markdown Every page is a Markdown file with a small header (front matter). The homepage: cat > content/_index.md content/posts/first-post.md content/posts/second-post.md <<'EOF' --- title: "Second Post" date: 2026-07-21 --- Another post - online right after the rebuild. EOF docker compose up -d --build Because the build takes only seconds and everything lives in the same folder, you can comfortably put this project into a Git repository (e.g. your Forgejo) and have the rebuild triggered automatically on every push by an Actions runner - that's how Serverküche runs its own site. When things go wrong The container stays unhealthy . Almost always the IPv6 trap from step 4: the health check queries localhost but nginx only listens on IPv4. Switch to http://127.0.0.1/ . Check with docker inspect --format '{{.State.Health.Status}}' CONTAINER . Traefik shows 404 page not found (instead of your site). That's the Traefik 404, not the nginx one - Traefik finds no matching router. Most common causes: the container isn't healthy yet, the Host() rule has the wrong domain, or the proxy network isn't external . See the 502/404 chapter in Understanding Docker networks. All links and images are broken. The baseURL in hugo.toml doesn't match the real domain. Hugo bakes absolute URLs based on this value - fix it and rebuild. A post doesn't appear. Check the date in the front matter: if it's in the future, Hugo won't build the post (see the warning box in step 3). A draft: true also hides content in a normal build. theme "PaperMod" not found during the build. The theme folder is missing from the build context - usually because it's a Git submodule that wasn't copied, or a .dockerignore excludes it. Store the theme as a real folder as in step 1. Maintenance & backups - Updates are relaxed. There's no running software with security holes at runtime - only two pinned build blocks. Occasionally bump the Hugo tag ( hugomods/hugo:0.165.0 ) andnginx:1.31-alpine and rebuild; otherwise your normal update process handles it. Update the theme when needed with a freshgit clone . - The backup is trivial - but important. Your entire website is the project folder (Markdown, hugo.toml , theme, Docker files). It belongs in a Git repository and/or your Restic backup. You don't need to back up the generated HTML files - they're regenerated from the source at any time. - No database risk. Because there's no database and no login, the biggest maintenance burden of classic CMSes is gone. The only "state" of your site is the content you write yourself - versioned in Git, it's both backup and change history. This post first appeared on serverkueche.de. Top comments (0)

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.