Skip to content

Deployments and Database Migrations

When having database migrations, or any other steps that need to happen in sync with a new deployment, you should make sure that the code is compatible with zero downtime (gradual) deployments.

If you have a very small application without a lot of users or clients, you could probably just migrate the database, change the code, and deploy it all at once, and it would work fine.

The new version of the code would replace the old one, and since there are not many users, they would probably not even notice at which point the deployment happened.

But you probably want to make sure your app can handle all the users and clients that you want to get.

There are just a couple of things that you should have in mind in your code. The rest will be done by FastAPI Cloud.

FastAPI Cloud scales your application automatically based on requests.

When you have many requests, it will automatically start more instances of your app to handle the load.

When you are deploying a new version of your app, at some point it would have to terminate all those old instances of your app and replace them with new ones.

To avoid downtime, FastAPI Cloud does this gradually.

It will start new instances of the app deployment with the new code, when there are new requests, it will give those new requests to the new instances.

It will let the old instances finish the requests they are handling, and then terminate those old instances.

In the end, there will only be the new instances running with the new code.

This gradual deployment means that at some point, both the old and the new versions of your app could be running at the same time.

And this is the main thing you should have in mind to properly handle zero downtime deployments and database migrations.

Your database will have one specific state at any point in time.

But you could have old and new code interacting with that same database with that specific state.

The main point to have in mind is that when you create a new database migration, it should still be compatible with whatever is the old code that is currently running.

For example, if you are adding a new column to a table, you could create the migration and execute it before the new code that will use it is deployed.

The old code won’t be using that column, so it will be compatible with the new database column.

Then you can execute the migration that adds the new column.

And then deploy the new code that requires the new database column that was just created by the migration.

If you are removing a column, the old code would probably break, because it would expect that column to be there.

So, what you can do is, first update the code to not use that column anymore.

Deploy that new code version, before removing the column from the database.

And once that version of the code is deployed and running, you can create a new migration that removes that column and execute it, after the new code that no longer uses the column is already deployed.

This is the main trick you need to have in mind.

Depending on the changes you need to do in your database, you will need to run them before deploying the code for those changes or after.

If you have in mind that the old version of the code and the new version need to both be compatible with the database, it will help to think about.

In many cases, the primary concern is simply adding or removing things from the database.

In most cases, you can have a simple rule of thumb:

  • Add: run the migration before deploying the code.
  • Remove: run the migration after deploying the code.

This way, in both cases, the old and new versions of the code will always be compatible with the database.

And this brings the second part of the rule of thumb:

Try to avoid migrations that do both add and remove data at the same time.

If you need to make changes that involve both adding and removing elements, handle them in stages: first perform the addition, deploy the corresponding code, and run any required migrations. Then proceed with the removal, deploy the updated code, and migrate the database again.

Doing these migrations and deployments in stages will help ensure that your app is always working.

When doing database migrations and deploying new code versions, keep in mind that both the old and new versions of the code could be running at the same time.

Sometimes it’s more important how the code evolves through time than the specific state at any point. 🛣️