How This Site is Self-Hosted With Docker
Most consulting sites live on Squarespace or WordPress hosting. This one runs on a server in my living room. Here's why, and how.
TL;DR: This site runs on a local Ubuntu server behind Cloudflare using Docker, a static site generator, and automated CI/CD. It costs almost nothing to run and uses the same infrastructure patterns I deploy for clients.
Why Self-Host a Business Site
I'm an IT infrastructure consultant. If I'm going to tell clients I can manage their systems, I should be able to manage my own. Self-hosting this site lets me practice what I preach - proper CI/CD, reverse proxy configuration, SSL termination, DNS management, and container orchestration. The same skills I use on client projects.
It also costs next to nothing beyond the domain registration and electricity I'm already paying for.
The Stack
The site is built with Eleventy (11ty), a lightweight static site generator. It takes Markdown files and Nunjucks templates and spits out plain HTML. No database, no CMS, no JavaScript framework. The entire site is under 14KB per page.
For serving, I use Fastify - a Node.js web server. It handles static file serving, the contact form API, rate limiting, and security headers. Fastify sits behind Nginx Proxy Manager, which handles SSL certificates and reverse proxying.
The whole thing runs in a Docker container on an Ubuntu server.
What is a Reverse Proxy?
A reverse proxy is a server that sits between the internet and your internal services. It receives incoming requests, determines which backend service should handle them based on the hostname, and forwards the traffic. It also handles SSL certificates so your internal services don't have to. For businesses running multiple web applications on one network, a reverse proxy is how you expose them all securely through a single public IP address.
How Traffic Gets Here
The path from a visitor's browser to this server looks like this:
Browser -> Cloudflare (DNS + proxy) -> My router -> Nginx Proxy Manager -> Docker container
Cloudflare is the registrar, DNS provider, and CDN proxy for etheriontech.com. All traffic hits Cloudflare first, which provides DDoS protection and caches static assets. A DDNS updater container keeps Cloudflare pointed at my current public IP, since residential internet doesn't come with a static address.
Nginx Proxy Manager (NPM) runs on the local network and handles SSL termination. It receives traffic on ports 80/443, matches the hostname, and forwards to the right container. This is the same setup I use for every service I expose externally - one reverse proxy managing certificates and routing for everything.
The Docker container listens on an internal port. It never touches the public internet directly.
Deployment
Source code lives in a self-hosted Gitea instance (also running locally). When I push a commit, Gitea Actions kicks off a CI/CD pipeline that:
- Runs automated test, quality, and security gates against a specific revision
- Builds a container image from that same tested revision
- Deploys the immutable image through the hosting environment
- Checks the public site and restores the last known-good release if validation fails
The whole deploy takes about 15 seconds from push to live. There are no FTP uploads or manual file copies, and the deployed image traces back to the exact commit that passed the checks. I still keep the public-site check and rollback in the pipeline because a container starting successfully does not prove that a visitor can use the site.
Security Hardening
Even a simple static site deserves proper security headers. Fastify runs with a Content Security Policy that restricts scripts, styles, and connections to an explicit allowlist. The contact form API is rate-limited to 10 requests per minute per visitor, while static pages and assets are excluded from that application-level limit. That is enough for a person submitting the form and low enough to slow basic spam without throttling the whole site.
The www.etheriontech.com subdomain returns a 301 redirect to the bare domain. One canonical URL, which is what search engines want to see.
Lessons Learned
DDNS is essential. Residential IPs change. Without automatic DNS updates, the site goes down silently the next time your ISP rotates your address.
Reverse proxy is the right pattern. Running multiple services on one public IP means you need hostname-based routing. NPM makes this straightforward - each service gets its own subdomain and SSL certificate. This same approach works for connecting multiple office locations - one firewall routing traffic to many internal services.
Static sites are fast. No database queries, no server-side rendering, no cold starts. Eleventy generates plain HTML at build time. Combined with Cloudflare's cache, the site loads in under a second globally.
Keep it simple. I could have used Kubernetes, or set up a multi-node cluster, or built a React frontend. But the job of a consulting website is to load fast, look professional, and not go down. A static site generator and a lightweight server handle that perfectly.
My recommendation for a site like this is to start with static HTML, an explicit CSP allowlist, and rate limiting on the API routes that accept input. Add infrastructure only when a measured requirement calls for it.
Would I Recommend This?
For most businesses, no. Use managed hosting and spend your time on your actual business. But if you're in IT and you want to demonstrate that you can build and maintain infrastructure, there's no better proof than running your own site on your own hardware.
If you're planning an infrastructure project and want someone who practices what they preach, let's talk.
