May 20, 2025 • Projects
May and June have been busy months for me. One of the things I managed to achieve was setting up self-managed VPS hosting. When I first decided to move to self-managed VPS hosting I considered Coolify, twice. After two failed attempts (despite watching brilliant videos by CJ) I decided to use Claude.ai to help me. I upgraded to Pro Max for the month to enjoy 20 x Claude uses and set about moving my hosting from cpanel to VPS hosting.
Thanks, for sharing:
Why Move?
I found my current cpanel hosting restrictive. I kept having to get in touch with support for simple things like needing a SSH terminal for each site I put up. Sites were running slowly and all support could say was "Upgrade and pay more" but I knew slow running sites aren't always solved that way. What I needed was to get away from shared hosting and run my own.
What I Chose
I chose Hetzner for its excellent value and performance, and paired it with HestiaCP for user-friendly server management. The setup process was more involved than shared hosting or managed services, but it gave me full control, something I needed for hosting Django applications, static sites, and APIs efficiently. Plus the monthly financial savings of managed (£49 per month) compared to self-managed (£7 a month) was worth the set up effort.
This post breaks down the critical steps, tools, and mistakes to avoid, based on my experience. If you're thinking of setting up your own VPS, here’s what to know before you dive in.
Hetzner’s CPX21 plan was my pick—affordable and powerful enough for a production-ready setup. With 3 vCPUs, 4GB of RAM, and 80GB NVMe SSD, it handles Django, PostgreSQL, and HestiaCP comfortably. I also considered proximity to my user base and chose a European datacenter.
I wasn't expecting the ability to host ALL of my sites on this one plan. I really thought I would need more but as it turns out there was enough space for everything and upgrading to the next level is simple. At £15 a month that is still way below what I was paying before.
Key VPS selection considerations:
Minimum 4GB RAM if you're running multiple services
NVMe SSD for faster file access
Regular backups or snapshots
A provider with good documentation and console access (Hetzner excels here)
I stuck with Ubuntu 22.04 LTS for long-term support and compatibility with HestiaCP. Before installing anything, I created a non-root user, disabled password login, and enabled SSH key authentication.
You can automate this entire setup using cloud-init during server creation, which saves time and reduces errors by pre-installing packages, creating users, and enabling security updates.
Before touching web stacks, I:
Set up a firewall through Hetzner Cloud
Installed fail2ban for brute-force protection
Disabled root login over SSH
Installed only essential packages
This might seem like overkill at first, but getting locked out (which happened to me) or having an open port on a production server is not something you want to experience.
HestiaCP is a lightweight control panel that simplifies domain, database, and SSL management. I installed it with Apache and PostgreSQL (but without MySQL), and immediately configured:
A secure admin account
Two-factor authentication
SSL for the HestiaCP panel itself
A custom hostname
A common mistake here is to start experimenting with SSL before DNS propagation completes. Let’s Encrypt has strict limits (5 certs per week), and a misstep can leave your production domain offline with no SSL coverage. Always test on a subdomain first.
Here’s where things get technical.
HestiaCP uses Nginx + Apache by default, but for Django apps, I needed to use Unix sockets and Gunicorn. That meant creating custom nginx proxy templates for each project. The default ones don’t work across multiple Django apps because the socket paths are hardcoded.
Lesson learned: Every Django app needs its own .tpl and .stpl file with the correct socket and static/media paths. Otherwise, you’ll run into 502 Bad Gateway errors.
I also created systemd service files to run Gunicorn processes and manage them like any other service.
One of the hardest lessons was understanding Linux permissions across users like:
My main login user
The HestiaCP user
The web server www-data
To solve permission errors, I created a shared group django-devs and made sure all project directories were owned by the HestiaCP user. I then gave the socket file permissions 666 so Nginx could access it.
Here’s a quick list of issues I ran into and how to fix them:
Problem: 500 Internal Server Error
Cause: Missing Domain in allowed_hosts
Solution: Add domain/IP to Django settings or .env
Problem: 502 Bad Gateway
Cause: Gunicorn service down or socket missing
Solution: Restart the service, check socket permissions
Problem: SSL error
Cause: DNS not ready or rate limit hit
Solution: Use staging domains for testing
Problem: Template defaulting
Cause: HestiaCP applied the wrong nginx template
Solution: Use v-change-web-domain-proxy-tpl command
Fixing these taught me how critical template management is with HestiaCP and Django.
I set up a daily backup script that archives:
Website files
HestiaCP user configs
PostgreSQL databases
Backups are stored in a compressed format and cleaned up every 7 days to save space.
I also have a script that I run every time I log into my vps hosting to ensure everything is kept up to date. I do this straight after logging in so I don't forget. I tend to run the backup at the end of the day.
Using Claude.ai I built a monitoring script that:
Checks each domain for 200 OK responses
Sends an email alert if a site goes down
Sends a recovery notice when it's back up
It runs every 5 minutes via cron, ensuring I know within moments if something breaks.
After getting myself locked out when I changed permissions, I avoided future lockouts by always keeping a second SSH session open while testing permission changes. If something goes wrong, there are three main ways to recover:
HestiaCP Panel – if accessible
Hetzner Web Console
Hetzner Rescue Mode – mount the disk, fix SSH keys manually, reboot
A simple chomd 777 on your home folder can block SSH entirely. Knowing how to fix ownership and permissions saved me from disaster more than once.
Self-managing a VPS takes patience, documentation, and a willingness to debug. But it also gives you complete control—no cPanel lock-ins, no unnecessary bloat, and full flexibility over how your apps run.
What started as a weekend experiment has turned into a robust hosting setup for Django projects, APIs, and static sites, all running under one panel. If you're thinking of doing something similar, I hope this summary helps you avoid some of the mistakes I made. I recommend the CJ videos on YouTube as a way to get started.
If you need a technical virtual assistant to do this for you, get in touch.
Thanks, for sharing: