Upgrading an old production server is not a popular topic today. Most run Docker, Kubernetes, or at least keep OS fresh. But sometimes you inherit a legacy VM, full of configs, and you just need to push it to a modern LTS without breaking anything.
I had exactly this case on DigitalOcean. Here's a quick, real-world rundown of what I actually did.
The Problem
The project was running on Ubuntu 20.04 with an outdated Nginx. Direct upgrade to 24.04 LTS is not possible. I needed a way to bring everything up-to-date quickly and safely and keep downtime to minimum.
Why It Matters
Those kinetic and lunar are eol versions. They no longer receive security updates. Running outdated versions exposes your server to vulnerabilities and leaves you without support. The jump to 24.04 LTS is significant - you get five years of support, better performance, and peace of mind.
How I Did It
To avoid downtime, I leaned on DigitalOcean snapshots + temp droplet. Then I upgraded the system in steps:
20.04 → 22.10 → 23.04 → 23.10 → 24.04 lts + Nginx upgrade
It's a bit long, but it gets the job done if you're stuck on an old upgrade path.
1. Prep the Server (No Downtime Yet)
First things first:
- Take a snapshot of your existing droplet.
- Use that snapshot to create a temporary droplet.
- Set up Floating IP so you can point your domain to this temp droplet when it's ready.
This gives you a safe rollback if something goes wrong.
2. Do the Upgrades
Upgrade to Ubuntu 22.10
sudo sed -i 's|http://mirrors.digitalocean.com/ubuntu/|http://old-releases.ubuntu.com/ubuntu/|g' /etc/apt/sources.list && \
sudo sed -i 's|http://security.ubuntu.com/ubuntu|http://old-releases.ubuntu.com/ubuntu|g' /etc/apt/sources.list && \
sudo apt update && \
sudo apt upgrade -y && \
sudo apt dist-upgrade -y && \
sudo apt autoremove --purge -y && \
sudo apt install -y ubuntu-release-upgrader-core && \
sudo sed -i 's/kinetic/lunar/g' /etc/apt/sources.list && \
sudo apt update && \
sudo apt upgrade -y && \
sudo apt dist-upgrade -y && \
sudo apt full-upgrade -y && \
sudo apt autoremove --purge -y && \
echo -e "\nUpgrade complete! Rebooting now...\n" && \
sudo reboot
Upgrade 23.04 → 23.10
sudo sed -i 's|http://old-releases.ubuntu.com/ubuntu|http://old-releases.ubuntu.com/ubuntu|g' /etc/apt/sources.list && \
sudo sed -i 's/lunar/mantic/g' /etc/apt/sources.list && \
sudo apt update && \
sudo apt upgrade -y && \
sudo apt dist-upgrade -y && \
sudo apt full-upgrade -y && \
sudo apt autoremove --purge -y && \
echo -e "\nUpgrade complete! Rebooting now...\n" && \
sudo reboot
Upgrade 23.10 → 24.04 LTS
sudo sed -i 's|http://old-releases.ubuntu.com/ubuntu|http://archive.ubuntu.com/ubuntu|g' /etc/apt/sources.list && \
sudo sed -i 's/mantic/noble/g' /etc/apt/sources.list && \
sudo apt update && \
sudo apt upgrade -y && \
sudo apt dist-upgrade -y && \
sudo apt full-upgrade -y && \
sudo apt autoremove --purge -y && \
echo -e "\nUpgrade complete! Rebooting now...\n" && \
sudo reboot
Now you're on Ubuntu 24.04 LTS. Double-check with:
lsb_release -a
3. Upgrade to the Latest Nginx
Don't skip this backup step:
cp -r /etc/nginx /root/nginx-backup
Then install the latest Nginx package:
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg --dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu noble nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install nginx
nginx -v
You should see version ^1.28.0. Reload your configuration to make sure it still parses:
sudo nginx -t
sudo systemctl restart nginx
4. Point the Floating IP Back
Wait a few minutes. Check your app. Run your health checks, monitoring, whatever you've got. If you have automated tests, don't forget to run them.
Look at logs and metrics make sure it's all good. Everything working? Cool. Move to the next.
Once you're sure everything's stable, switch the Floating IP back from the temp droplet to your freshly upgraded one.
Hang on to the temp droplet and snapshot for few days. If nothing catches fire, you can remove them.
Timeline
Total time: about one hour. Most of that is just waiting for reboots and packages to download. You can grab coffee and check on progress every few minutes.
Final Thoughts
This isn't a workflow I want to repeat often. If you can move to Docker or rebuild your server from scratch — do it.
But sometimes legacy systems don't give you that choice.
And when you must upgrade an old Ubuntu machine step by step, this method works. Fast, safe, and almost zero downtime.
Author's Note
Thanks for sticking around!
Find me on dev.to, linkedin, or you can check out my work on github.
Notes from real-world Laravel.
Top comments (0)