The Developer's Guide to Avoiding PaaS Lock-in

Jeff Morhous
@JeffMorhousFiguring out where and how to host your app can be scary! One thing that may bring you stress sooner or later is the thought of migrating from one provider to another. If migration is easy, choosing a host becomes a reversible decision rather than a permanent commitment. In this guide, we’ll explore how to keep your infrastructure flexible so you can take advantage of the best features, reliability, and pricing that various PaaS providers offer – without getting trapped by PaaS lock-in.
First, we’ll first look at why you might want to switch PaaS providers and how much difference it can make. Then we’ll dive into specific strategies to avoid lock-in, from using external databases to containerizing your app. Let’s get started!
✅ Tip
Knowing when and how to migrate between cloud providers is just another arrow in the quiver of being PaaS-Ops fluent!
Why being able to switch PaaS providers is helpful
Before getting too deep into the how, it’s worth understanding why you should put effort into avoiding PaaS lock-in. Heroku is no longer the only sherriff in town - it might pay off to take advantage of the competition in the space.
Going where the features are
One big benefit of being flexible with your provider is features. No single PaaS has every feature under the sun. One platform might offer global deployment, built-in CDN, or a specific add-on you need, while another might lack those. Features might change over time!
If your current host is missing something important (or a new service launches with a feature that’s perfect for you), switching may unlock new capabilities for your app.
Prioritizing reliability
Even trusted PaaS platforms like Heroku can have outages or poor performance from time to time. If your provider has frequent downtime or poor reliability, that has a direct impact on your users. If reliability on your originally chosen platform becomes a consistent issue, you will be super glad you set yourself up for success by avoiding lock-in might improve stability (or at least give you peace of mind that you’re on more solid ground).
Going where the cost savings are
Pricing is often the loudest reason teams start looking elsewhere. The developer experience that PaaS offers comes at an expense, but that expense varies widely. Depending on your compute needs, you might be shocked how much pricing can vary between the most popular PaaS providers.
☝️ Use our PaaS pricing comparison calculator to compare compute costs for any size app! Hosting cost differences can be massive, and it pays to be able to switch platforms without issue.
Use third-party hosted databases
Data lock-in is just as real as PaaS lock-in Databases are one of the hardest things pieces of a service to move. If your database is tightly coupled to your PaaS provider (for example, using a proprietary database service that makes it hard to export data), migrations can become nightmares.
If you’re on Heroku, you might be using Heroku Postgres. While that’s a convenient one-click add-on, you could instead use a dedicated managed database like Crunchy or Timescale. Your app will connect to the database over the internet, using a connection string, regardless of where the app is running.
If you want to migrate where you host your app, you can migrate the app and just point it at the existing database. No downtime!
Don’t use platform-native schedulers
PaaS platforms often provide a native way to schedule jobs. Heroku, for example, has the Heroku Scheduler add-on that lets you configure tasks to run every so often. Don’t let the convenience here tempt you!
While convenient, these platform-specific schedulers are another way to get locked-in. If you rely on a scheduler associated with particular platform, migrating means you have to reconfigure all those scheduled tasks in a new scheduler on the target platform. This is not only painful, but hard to pull of without downtime or lost background jobs.
Keep your job scheduling in your application code. Instead of using Heroku’s scheduler, you can use a clock process or an embedded scheduler library that runs as part of your app deployment. Most languages and frameworks have solutions for this often baked into background job processing. You can also run a dedicated clock process, which is easy enough to set up in your Procfile.
The idea is to treat scheduling like any other piece of your application logic. Background jobs should be under version control, tested, and platform-independent.
Avoid long-term contracts
When your compute needs grow, PaaS providers may try to entice you into a longer-term enterprise contract. Usually, this means committing to a year (or multi-year) of usage, often in exchange for a discount or “extra support”. It sounds like a good deal, but be very cautious. Enterprise contracts can severely restrict your flexibility and even come back to bite you with unexpected terms.
We’ve had many Judoscale customers have share regret after signing year-long deals with Heroku. If you sign a long-term contract, you are at the mercy of that provider. Even if the technical migration of your app is easy, you might be financially or legally tied down, unable to leave without paying a hefty penalty or forfeiting discounts.
At the very least, keep in mind the tradeoff. A tempting discount is explicitly a way to lock you in. If maintaining the freedom to switch is important for you, it might be worth paying a bit more.
Use Docker!
Containers are often thought of as the cure for the “it works on my machine” problem. Docker has become a standard way to ensure an application runs the same regardless of the underlying host. If you package your app in a Docker container, you ideally can run that container on any platform that supports Docker.
This can improve portability because moving your Docker container from one host to another is much simpler than trying to reconfigure infrastructure. Still, it comes at a cost. If you don’t already have your application ready for Docker, it takes some work! There’s also some operational overhead (building and pushing images, tags, etc). This is probably the biggest lever you can pull to become PaaS-flexible, but it’s also probably the hardest lever to pull.
Bring your own deployment tools
One of the big selling points of platforms is that they let you deploy code without much extra work. Heroku paved the way with something as simple as git heroku push
.
These days Heroku offers Heroku Pipelines for continuous delivery: you push to a staging app, promote to production, etc. It’s a great developer experience, but you can probably guess what I’m going to say next!
If you build your deployment workflow around these proprietary tools, migrating to another platform means rebuilding your deployment pipeline from scratch.
Another area of flexibility you can invest in is deployment. Consider using platform-agnostic deployment tools. Instead of relying on Heroku’s pipeline, you can use external continuous integration services and scripts that deploy to Heroku (or any platform) via that platform’s API or CLI.
But let’s also be completely candid here—we still use and love Heroku Pipelines at Judoscale! It’s one of our favorite Heroku features, and we’re not going to simply throw it out in the name of portability. The point here is to be aware of the trade-off when you embrace a platform-native feature. Is the feature worth the lock-in?
Use a third-party autoscaler
Many platforms have built-in autoscaling - the ability to automatically add or remove instances/dynos based on compute needs. This is awesome for handling traffic increases while controlling cost. Autoscaling is another feature of platforms that is great, but can contribute to lock-in. If you rely on a platform’s built-in autoscaler, that logic might not transfer if you migrate.
In practice, native autoscalers often have limitations. Autoscaler’s like Heroku use response time to determine when to add or take away dynos, which is notoriously slow and unreliable. Using a third-party autoscaler like Judoscale solves both of these problems.
First, Judoscale scales based on request (or job) queue time. This helps you respond to changes in traffic faster, making your app more nimble. It’s a vital metric to actually understand and respond to server capacity, so an autoscaler that uses queue time is a huge upgrade.
Second, a third-party autoscaler helps you avoid platform lock-in. Judoscale has integrations for Heroku, Render, Railway, Fly, and even Amazon ECS. Using Judoscale instead of a native autoscaler is a great way to have one less thing to migrate.
What it’s actually like to switch platforms
If you’re wondering how much of this really matters, wonder no more! Judoscale migrated our own staging app, an eight-year-old SaaS from Heroku to Render to see what it takes. The video is a super interesting look into what it’s like to move a real app from one PaaS to another.
In the end, the ability to switch gives you options. It keeps providers honest (you can leave if they don’t meet expectations) and lets you take advantage of the best offerings in the market. So take some action now to make a PaaS move easier. Yes, there are tradeoffs! Much of what we talked about in this article takes away some of the benefits of platforms. Refusing to take advantage of some PaaS conveniences definitely comes at a cost, but the benefit is meaningful.