It begins - blog hosting and design decisions
Everyone has their traditions.
As a developer, I start all my projects with a “Hello world”, that’s why the first post is so small. As a person, as jdgomez, all my projects continue with “It begins (don’t cry)”, so here is the second entry of the blog.
Context
As many people I bought a Raspberry Pi long ago, and I discovered how good it was at storing dust. Recently I remembered why I bought it (yes, to host my own blog), why I became a developer and I decided to push that project as a way to learn what AI can do for me.
- Goal: Personal Blog self-hosted on a Raspberry Pi
- Stack: Hugo (static site generator in Go) + Nginx (web server)
- Agentic Coding tool: Claude Code
Why the stack
“No. I mean, I’m not a shop boy. I was just working in a shop.” - Tristan Thorn 1
I started learning how to code with C++ and you can be sure I miss it so much. Right now I am focused on learning Go and Python. So my first decision was to code my own web server. It would be created in Go, and it would serve as a great exercise.
Luckily for me, one of my current goals is to learn more about AI, and I checked my plan with Claude Code and… that wheel was already invented.
Disclaimer: I really love to reinvent wheels (maybe a topic for another entry).
Hugo is a really fast and small framework for building websites. It’s written in Go, and can generate HTML from Markdown files. I like Markdown as a standard for documentation and clean files, I don’t want to bother with HTML or CSS.I usually don’t like to use software in alpha versions and Hugo is in v0.161.1 right now. But this project is also about learning, and checking the Hugo GitHub page I discovered an active community with solid pipelines, so I am going to give it a try.
Nginx was a hard choice for me, because I have always been an ‘Apache boy’, but again, I am open to learn, so I asked Claude Code to do a deep research on performance. I usually over-engineer performance in my own code when the project constraints allow it, and I am a curious person, so why not check some data before choosing.
Here is the result:
Nginx vs Apache — Performance Benchmarks
RAM Usage
| Scenario | Nginx | Apache (Event MPM) |
|---|---|---|
| Idle | ~50–100 MB | ~60 MB |
| 100 concurrent connections | ~15–20 MB | ~40–60 MB |
| 1,000 concurrent connections | ~25–35 MB | ~150–250 MB |
| 10,000 concurrent connections | ~50–100 MB | ~2–5 GB |
At idle, both servers are roughly equivalent. The gap grows dramatically under load: at 10,000 concurrent connections, Nginx uses 20–100x less RAM than Apache.
Why: Nginx stores ~2–5 KB per connection (just a file descriptor and minimal state). Apache allocates ~14–20 MB per process or thread.
CPU Usage
- Nginx: ~120,000 req/s serving static files (modern x86 server)
- Apache: ~70,000 req/s — 1.7x lower throughput
- At 512 concurrent connections: Nginx 28,400 req/s vs Apache 15,200 req/s
Why: Apache generates thousands of context switches (one process or thread per request). Nginx uses a non-blocking event loop — a single worker handles 10,000+ simultaneous connections with no context switch overhead.
Disk Space
| Nginx | Apache | |
|---|---|---|
| Binary | ~1–6 MB (ARM: ~1.3 MB) | ~10 MB |
| Full installation | ~10–20 MB | ~50–100 MB |
Apache ships with a dynamic module directory (separate .so files), multiple configuration files, and per-directory .htaccess support. Nginx is more monolithic — modules compile into the binary, keeping the footprint small.
Raspberry Pi (ARM) — Real Hardware Numbers
Benchmark run directly on a Raspberry Pi:
- Nginx: 1,067 req/s
- Apache: 9 req/s
When Apache is configured with Event MPM and PHP-FPM (replacing mod_php), the gap narrows significantly (~108–109 req/s each). However, Nginx still wins on resource consumption across the board.
Conclusion: For serving static content (Hugo-generated sites), Nginx is the right choice on resource-constrained hardware like my Raspberry Pi.
For the domain I chose to drop all my current domains and trademarks, as they were intended for other purposes.
This is a personal blog, and I am a developer. It’s time to buy jdgomez.dev
I purchased it on Cloudflare because it’s cheap and can easily provide me with HTTPS and has an out-of-the-box tunnel to handle my dynamic address. My ISP offers a static IP option — that’s what I considered first — but at ~€144/year it makes no sense for a personal blog.
Steps taken
Usually I will post also the commands to achieve the result I am claiming, cause this is a blog about processes (mental and technical), not about goals.
Re-reading the article before publishing it, I realiced that the steps broke the flow and them don’t provide useful information (there are quite simple), but at the same time I don’t want to remove it, I am developer after all.
The solution is to hide them, check on it if you are interested.
Steps & commands
- Installed Hugo and Nginx via apt:
- Hugo v0.131.0 extended (arm64)
- Nginx 1.26.3
- Created Hugo site in
/home/pi/blog/withhugo new site blog - Configured
hugo.toml(baseURL, language, title) - Created minimal layout in
layouts/index.html(no external theme) with Hello World - Built the site with
hugo→ output in/home/pi/blog/public/ - Configured Nginx to serve
/home/pi/blog/public/on port 80 - Disabled the default Nginx site
- Granted execute permissions on
/home/pisowww-datacan read the directory - Verified access from local network → “Hello World”
Relevant file structure
/home/pi/blog/
├── hugo.toml ← site configuration
├── layouts/
│ └── index.html ← home layout (no external theme)
└── public/ ← Hugo build output (served by Nginx)
└── index.html
/etc/nginx/sites-available/blog ← Nginx config
/etc/nginx/sites-enabled/blog ← active symlink
Final considerations
Although I am Spanish, the master language for the blog should be English, “the developer’s language”, and include my native language as a secondary option. I am currently learning German, but it’s too early for that. I will want to add it when the time comes to put it into practice, but I don’t want to do automatic translations. I am who I am, and this includes my language.
Finally, I always liked Uncle Bob’s blog, clear and simple.
So I chose to have a minimal custom theme, created from scratch by Claude Code, with clear instructions:
“I am a developer that hates cookies, my eyes prefer dark themes.”
This was its solution:
- Background black (#0d0d0d), text green (#00ff88) — terminal aesthetic, easy on the eyes
- Monospace font (Courier New, system fallback)
- Blinking cursor in the header — pure CSS animation, no JavaScript
- Terminal-style header:
~/jdgomez blog $+ cursor - Zero cookies, zero JavaScript, zero external resources — no Google Fonts, no analytics, no third-party anything
I couldn’t love it more.
The end
That’s all. That’s how all this begins. In the future I plan to add better time metrics on how much time it cost me to complete these little projects, including the article writing, but I didn’t properly measure it this time, so let’s consider it an improvement for next articles.
Hope you enjoyed this.
Thanks for reading.