Skip to content

Migrations and Seeds

This guide provides different options for running database migrations, seeding databases, or loading any type of custom data into your datastores.

TIP

As best practice, we recommend assigning ownership of each database to a single application. This will ensure that the database schema and seeds are maintained in one place. By following this practice, you will be able to automate the execution of your migrations and seeds in a predictable way. You will also be able to automate the creation of additional environments including preview environments.

Automatic - On App Startup

Automate your migrations and seeds by adding an entrypoint to your Dockerfile. As your application starts up, the entrypoint script will run before your application starts. If there are any errors, the newly deployed version of your application will not start up and the previous version will remain running. This ensures that migrations and seeds are run with each deployment and allows you to keep your application code and database in sync.

Below is an example of this for a Rails application.

bash
FROM ruby:3.1.0

WORKDIR /root
COPY . .
RUN chmod +x ./entrypoint.sh

ENV PORT 80
ENV RAILS_ENV production
ENV RAILS_LOG_TO_STDOUT true
RUN gem install bundler && bundle install
EXPOSE 80

ENTRYPOINT ["./entrypoint.sh"]
CMD ["bundle", "exec", "rails", "server"]

The key line is line 13, which specifies the entrypoint script to run. ENTRYPOINT ["./entrypoint.sh"]

Next, create the entrypoint script.

bash
#!/bin/sh

set -e

echo "Running migrations..."
rails db:migrate

echo "Running seeds..."
rails db:seed

exec "$@"

WARNING

If you have more than one instance (container) for your app, your migrations will run multiple times. Ensure that your migrations and seeds are idempotent and can run in parallel.

Automated - Separate Task

Configure an application dedicated to running your migrations and seeds. This application can use the same Dockerfile as your main application, but will be configured to run a different command. This will ensure that your migrations and seeds are run only once per deployment.

To set up, create a new application in your stack. You can use the same repo as your main application. Migrations and seeds usually run very quickly and don't require a lot of resources, so you can typically use a serverless app. Provide a different Dockerfile so that you can run a different command - the command to run your migrations and seeds.

TIP

Be sure to add capabilities to connect this task to the datastore(s) you want to run migrations and seeds for.

Finally, to automate the execution of your task, you can just run the nullstone exec command. Add this command to the appropriate step in your CI/CD pipeline.

For more information on the nullstone exec command, see the docs here.

Manual - SSH into your application

Use the nullstone ssh command to log in to your app instances. Check out the details for this command here. Once you have logged in, you will have your application code available to you. Run your seeds and/or migrations using any tools or commands you choose.

The db endpoint and credentials are available in the environment variables. Use these to connect to your database. To view the environment variables in your instance you can run env or printenv from the command line once connected.

TIP

Both container apps and server apps can be accessed via SSH, however, you can NOT use serverless or static site apps because there is no instance to log in to.

TIP

Make sure that the app you ssh into has a capability for the datastore you are trying to access. The capability ensures that the instance has the correct ports opened, permissions granted, and environment variables injected into the instance.

WARNING

It is easy to forget to run migrations and seeds if done manually. Using this method requires the code with your latest migrations and seeds to be deployed before you can run them.

Manual - SSH into a bastion

Use an SSH command to connect to a bastion server and use it as a jump box to connect to other infrastructure in the network. For details on how to create a bastion server in your network, see the docs for configuring a bastion.

If you establish an SSH tunnel, you can run commands from your local machine and have access to your local files. You can also connect tools like pgAdmin to your database through the tunnel.

TIP

Make sure that the app you ssh into has a capability for the datastore you are trying to access. The capability ensures that the instance has the correct ports opened, permissions granted, and environment variables injected into the instance.

WARNING

It is easy to forget to run migrations and seeds if done manually. Using this method requires the code with your latest migrations and seeds to be deployed before you can run them.