Skip to content

Migrations and Seeds

A preview environment is only as good as the data loaded into it. It is best practice to have a fresh set of data loaded for each environment in order to produce predictable results. In some cases, this might be a static set of data defined in a seeds file. For other use cases, you might want to fetch some production data and scrub it before loading it into your preview environment. No matter what data you want to load, you will always want to make sure that the database schema is up-to-date.

Best Practices

When setting up database migrations and seeds, be sure to keep the following best practices in mind to avoid issues.

  1. Each database should have an owner who is responsible for maintaining the database schema and seeds. If more than one application is making changes to a database, it can cause conflicts or unpredictable results.
  2. If possible, use a tool that can run migrations or seeds in an idempotent manner. It should execute each update in a transaction and block any competing updates until it has finished. You don't want to have more than one process trying to update the database schema at the same time.

Automation Options

Nullstone provides a few different ways to run database migrations and load data into your preview environment. Each option along with their benefits and drawbacks are described below.

On Boot

If your application is a container application, you can add an entrypoint that runs before your application boots up.

When To Use
  • If you want to run migrations and seeds each time your application deploys, for each code commit and deploy.
  • This is not the best option if you want to load data one time when the preview environment is launched.
Benefits
  • If the migrations or seeds fail, the application will not start; and the previous version of your application will remain running.
  • It is guaranteed to run before your application starts up each time.
Drawbacks
  • It runs for every instance of your container. If you have more than one instance, the migrations and seeds scripts will attempt to run at the same time. You will need to make sure that these are idempotent and can run in parallel.
Configuration

To use this option, first add an entrypoint to your Dockerfile. 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

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

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

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

exec "$@"

Triggered Job

Nullstone provides webhooks that can be used to trigger a custom job for loading data into your preview environment. To utilize this option, provide a URL that can be subscribed as a webhook callback. For loading preview environment data, you can subscribe to the first-deployed event so that it runs once your application is ready.

TIP

Check out the webhook docs to view all the events and learn how to configure webhooks.

When To Use
  • If you want to run migrations and seeds a single time when the preview environment is first launched and code deployed.
  • When you need to load a custom set of data into your preview environment.
Benefits
  • Allows you the full flexibility to run any logic you need.
  • Is automatically triggered when the preview environment is launched.
Drawbacks
  • Only runs once when the preview environment is launched.
  • If the job fails, you will need to fix and re-run the job in order to ensure your data is loaded.
Configuration

To utilize this option, you can use any job architecture you want. The only requirement is to provide a URL that receives the webhook callback to queue up a job.

Example architectures:

Webhook Callback EndpointJob Execution
Rails ActiveJob with Sidekiq/ResqueYour registered endpoint will receive the callback and inserts a job into the queue.The sidekiq/resque system will pick up and execute the job.
Serverless Lambda using an SQS QueueYour registered endpoint will receive the callback and insert an event into an SQS queue.Create a Lambda application and subscribe it to the same SQS queue. When the event is inserted, the Lambda will execute. This is a good option for short running jobs that take less than 15 minutes.
ECS Task using an SQS QueueYour registered endpoint will receive the callback and insert an event into an SQS queue.Create an ECS Task application and subscribe it to the same SQS queue. When the event is inserted, the ECS Task will execute. This is a good option for longer running jobs.