For several weeks, my personal blog was proudly running on a Raspberry Pi 5. It was more than just a functional website, it was a symbol of learning, tinkering, and building something from the ground up. But as the project grew and my ambitions expanded, it became clear that it was time for an upgrade. This post is a detailed account of how I migrated my Ghost blog from the Pi to a dedicated VPS with a static IP, why I made the switch, and what I learned throughout the process.
Why I Started on a Raspberry Pi
From the beginning, I wanted to challenge myself by avoiding "one-click" solutions. Instead of using a prebuilt hosting service, I wanted to understand what really happens under the hood when you host a website. The Raspberry Pi offered the perfect playground: small, silent, low-powered, and running Linux, just like a production server would.
Along the way I learned how to set up Ghost via CLI, run a reverse proxy with NGINX, use Let's Encrypt for HTTPS certificates, deal with DNS and dynamic IPs using DuckDNS, configure firewall rules and SSH for remote access, and maintain uptime with automated backups.
It wasn't perfect, but it worked. And most importantly, it taught me the fundamentals of server management.
The Downsides of Hosting on a Raspberry Pi
Despite the great learning experience, some limitations became increasingly frustrating. My ISP assigned me a dynamic IP, and even with DuckDNS and scripts to update the A record, there were still occasional downtimes. The router itself had quirks too - sometimes it would restart, or port forwarding would stop working correctly, knocking the blog offline until I manually fixed it. On top of that, the Pi had clear performance ceilings: it handled light traffic well, but it was never meant to be a production-grade server, and file I/O and database queries were slower than on SSD-backed infrastructure. Worst of all was the remote access pain - if something went wrong while I was away from home, there was no way to fix it unless I was on the same network.
That's when I decided to move everything to a virtual private server - a clean, stable solution that gives me full control without the physical constraints of home hardware.
Why I Chose a VPS with a Static IP
One of the core motivations behind this migration was to eliminate downtime caused by my dynamic home IP. The new VPS comes with a static IPv4 address, which means I no longer have to worry about DNS updates or broken port forwarding. It's always online, fast, and connected directly to the internet backbone.
Beyond that, the server sits in a data center with reliable power and connectivity, offers better performance through SSD storage and stronger CPU resources, is easier to manage with SSH access from anywhere and better tooling support, and comes with proper security options like system-level firewalls, automated updates, and TLS encryption via Let's Encrypt.
Preparing the VPS Environment
I spun up a fresh instance of Ubuntu 24.04 LTS and ran through a complete setup from scratch.
Initial Security Configuration
I updated system packages, set up a firewall (UFW) with only SSH and HTTP/S open, and created a dedicated user for managing the Ghost blog.
Installed Required Software
I installed Node.js v18.20.8, the version officially supported by Ghost, set up MySQL 8 with a dedicated database and user for Ghost, installed NGINX to handle SSL and proxy requests to Ghost, and pulled in Ghost-CLI, the official tool for managing Ghost instances.
Set Up the Domain
I updated the DNS records (A and AAAA) to point to the new static IP, then used Ghost CLI to auto-configure NGINX and request SSL certificates from Let's Encrypt.
Initial Ghost Setup
I installed Ghost in /var/www/ghost using ghost install and verified that the blog launched successfully with the default content.
Migrating My Blog Content
The real heart of my site - the posts, images, and theme, lived in the content/ directory of my Raspberry Pi. Inside this folder sit a few key elements: data/ghost.db, the SQLite database containing all blog posts and settings; images/, the uploaded media files; themes/, the custom theme files, including the one I modified (solo); and logs/, useful for debugging if needed.
I shut down the Ghost process on the VPS and securely copied the entire content/ folder from the Pi using scp over SSH.
After the transfer, I replaced the default content folder on the VPS, set ownership to the correct ghost user and group using chown, and restarted Ghost with ghost restart.
Everything came up immediately. My site - posts, layout, styling, everything, looked just as it had before, now served from a much more robust machine.
Small Adjustments After the Move
After the successful launch, I made a few final tweaks. I edited the theme's index.hbs to remove the post limit by adjusting the {{#foreach posts limit="6"}} helper to either increase or remove the limit. I verified HTTPS renewal with:
certbot renew --dry-runAnd I tested SSH access and backups from multiple locations to ensure remote maintenance would be smooth.
Final Thoughts
This migration taught me a lot more than I initially expected. Hosting on a Raspberry Pi is an empowering experience, you get to see how every layer fits together, from DNS and HTTP to SQL and theming. But a VPS makes sense when you're ready for more stability, control, and headroom for experimentation.
I now have a production-grade environment that I can build on. Whether I add a secondary Ghost instance, spin up an internal admin panel, or test self-hosted security tools, the VPS gives me that flexibility.
But I'll never forget how it started - on a tiny board next to my router, blinking away as I learned one command at a time.