I just spent two days finishing migrating the last of my old websites and services off my old server and into new homes. This project has been on my todo list for over two years, and I've been making slow progress since then, mostly by spending a day on it here and there.
I finally finished moving the last things off the server today. The last two things left were my Twitter streaming search script and a really old MediaWiki website. Both were running on a 6-year old operating system, and under deprecated versions of Ruby and PHP. The MediaWiki instance had been EOL since 2012. The OpenSSL library was so old the Heartbleed bug hadn't even been written yet. Other things on that server were a Gitlab install (which I replaced with Gogs in February), my own SSL root authority (which is significantly less useful now that Letsencrypt makes certificates free and easy), an old web app I haven't used since 2009, and many cobbled together scripts.
This was quite a painful process all told, and I would like to avoid getting into this this situation in the future. Here are some things I'm planning to do that will help make the upgrade process easier in the future.
Upgrade regularly
Spend the time it takes to upgrade the whole server (OS as well as application dependencies) every 3-6 months, instead of letting the machine get to be several years old. The whole reason I got into this situation in the first place is the OS was so old incremental upgrades were no longer possible. Updating the OS, PHP/Ruby versions, and applications regularly means smaller incremental changes rather than getting into a situation where your app no longer runs because the scripting language changed so much since the last upgrade.
Separate the web and database servers
I've had a separate database server for a long time. The main benefit this gives me is I'm able to move the source code for a website to a new server without having to also worry about migrating the database at the same time. It's one less moving part in the migration process, and I will continue to do this in the future.
Productize everything
Some of these projects and websites were so old that I didn't actually have them in Git repos yet. Some of them were in Subversion, some were just piles of files on disk. A few of them shared a common set of files on the server, rather than using any sort of package management and proper dependency chain. Of course my more recent projects are much better packaged, but I had forgotten I had not always done things this way. Making each project a self-contained application with its own Git repo makes it easier to move them around to new servers later.
Avoid using servers in the first place
These days, we have many options for running applications without dealing with the underlying operating system. Google AppEngine (on which I currently run indiewebify.me and xray.p3k.io), Amazon Lambda (where I run ca3db), and Heroku to name a few. Once a service is running in infrastructure such as these, you can forget worrying about the underlying operating system updates, and focus on your application logic. The service providers will handle everything under the hood and you can forget that upgrading OpenSSL is a thing that needs to happen.