Lessons from Building My Portfolio Website (mihirsavla.com) — A Deep Dive into Docker, Apache, and DevOps on a VPS

Building a personal website might seem straightforward — pick a template, write some HTML, host it somewhere, and you’re live. But when you choose to do it the engineering way, self-hosted on a VPS with full control, it becomes a project that challenges your understanding of servers, Docker, networking, reverse proxies, and deployment pipelines.
Here’s what I learned (and wish I had known earlier) while building mihirsavla.com:
✅ What Went Right
1. Dockerized App Architecture:
I built my site as a containerized app using Docker with Nginx.
Benefits: Portability, version control, clean local testing, and easy rebuilds.
2. Blog Integration with Hashnode API:
Instead of manually updating blog content, I used Hashnode’s GraphQL API to dynamically fetch and display blog posts on a
/blog
route.Interactive, live, and always up-to-date without deploying changes for every post.
3. GitHub Actions for CI/CD:
Every push to GitHub triggers an automated deploy to my VPS via SSH keys.
I used GitHub Secrets to store my SSH private key securely.
This saves tons of manual effort and is incredibly satisfying when you see it "just work."
4. Reverse Proxy via Apache on Port 8080:
My VPS serves all domains through Apache on port 8080 (not the default port 80).
I added a custom
VirtualHost
rule using WHM’s Include Editor to reverse proxy mihirsavla.com to my Docker container running on port 8181.
🧠 Challenges That Made Me Wiser
1. Port Conflicts from Existing Services:
A previous script I wrote was using
python3 -m http.server
bound to port 80, which caused Apache to fail silently.Lesson: Always check
lsof -i :PORT
andsystemctl list-units
to find conflicting services.
2. Misleading Success Signals:
Apache restarted successfully, but my domain still served the old script’s response.
Lesson: Curl your domain and 127.0.0.1 at the correct port to validate what's being served.
3. Confusing Apache with Docker and cPanel:
Apache via cPanel uses many layers: pre-main, pre-virtualhost, post-virtualhost, userdata, etc.
The actual serving domain might not be reading your config if not added in the correct include.
Solution: Use
/scripts/rebuildhttpdconf
followed by/scripts/restartsrv_httpd
after every change.
4. Custom Userdata Folder Didn’t Exist:
For
mihirsavla.com
, I had to create a newproxy.conf
manually in/etc/apache2/conf.d/userdata/std/2_4/mihir/mihirsavla.com/
Restarting Apache with
restartsrv_httpd
finally worked.
❌ What Not to Do
Don’t bind services directly to port 80 unless you're certain nothing else needs it.
Don’t rely on WHM config alone — always confirm Apache is actually listening with
ss -tuln
.Don’t hardcode your deployment IPs — use a proper domain with reverse proxy and HTTPS if possible.
Don’t forget to test with
curl
andapachectl -S
.
💡 Pro Tips for Self-Hosting on a VPS
Always use Docker for static portfolio sites — it's overkill but educational and clean.
Use port mapping smartly (
0.0.0.0:8181->80
inside Docker).Keep a systemd script to restart essential scripts like your disk usage monitor.
Consider a static site generator or Next.js for future scalability.
🔧 Tools & Tech Stack
Docker & Docker Compose
Apache on AlmaLinux VPS (cPanel/WHM)
GitHub Actions (CI/CD pipeline)
Hashnode GraphQL API
SSH with key-based auth
curl, lsof, ss, systemctl
🎯 Final Thoughts
Building mihirsavla.com wasn't just about a website — it was about proving to myself that I could manage deployments, handle conflicts, automate everything, and deliver a product that reflects engineering maturity.
If you’re looking to learn DevOps through a real project — building and deploying your own site is one of the best ways to do it.
Happy to answer any questions or help anyone trying something similar! update me on LinkedIn
Subscribe to my newsletter
Read articles from Mihir Savla directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
