Improved migrations and Audit log management

Tuesday, February 11, 2025

node sdk cover

We've improved the way database migrations are handled in the Console, giving self-hosted users more control over how and when migrations are run. We've also added a utility to manage audit log retention.


Improved migrations

As we continue to ship new features, updates and improvements to Phase, we often need to make updates to the database schema in the Console. These schema changes are applied via migrations by Django, which powers the backend of the Phase Console. So far, these migrations were automatically applied when starting or restarting an instance of the Console. This strategy works fine for basic deployments, but for more complex multi-container deployments of Phase, this approach was problematic and could potentially cause unexpected database locks, race conditions or other issues that would lead to downtime.

To address this, we've reworked how database migrations are applied. Migrations can now be handled by a dedicated migration service, ensuring schema changes are applied in a controlled and predictable manner. In multi-container deployments, backend and worker services can now be configured to wait for migrations to complete before starting, reducing the risk of conflicts. Additionally, we've introduced an environment variable, EXTERNAL_MIGRATION, allowing deployments to explicitly control whether migrations should be applied externally or not. This value is false by default to preserve backward compatibility with previous versions of Phase.

For self-hosted single-instance deployments, no changes are required — migrations will continue to work as before. However, for multi-instance deployments, setting EXTERNAL_MIGRATION=true ensures that migrations are handled by the dedicated service, improving stability and reliability. Here's a template for the migration service:

migrations:
  container_name: phase-migrations
  image: phasehq/backend:latest
  command: python manage.py migrate
  env_file: .env
  environment:
    OAUTH_REDIRECT_URI: "${HTTP_PROTOCOL}${HOST}"
    ALLOWED_HOSTS: "${HOST},backend"
    ALLOWED_ORIGINS: "${HTTP_PROTOCOL}${HOST}"
    SESSION_COOKIE_DOMAIN: "${HOST}"
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_started
  networks:
    - phase-net

When using this migration strategy, make sure to configure your backend and worker service to wait for migrations to complete before starting by adding:

depends_on:
  migrations:
    condition: service_completed_successfully

Check out the standard docker compose template for a full example on setting this up.

For Kubernetes deployments, we've introduced managed migrations through a dedicated Kubernetes job and improved the deployment sequence with post-install/upgrade hooks. This ensures migrations are completed before the main application pods start running.

We've also improved the Redis integration with password authentication support and added readiness checks to prevent worker pod restarts.

Going forward, any releases that require migrations to be run will be marked with a Migration required label at the top of the release notes. Use this to determine whether or not to run migrations before starting the service. If you're using this strategy, you'll need to ensure that your workflow runs are configured to wait for migrations to complete successfully before starting.

Audit log cleanup

Self-hosted instances of Phase retain all logs permanently by default, but workflows that frequently poll for updated secrets can quickly generate thousands of log entries as Read events. Over time, this can lead to a gradual degradation in the performance of your instance. To help deal with this, we've added a utility to easily purge old log entries.

This utility is run via a Django management command from either the backend or worker containers. Simply shell into your container with:

docker exec -it <container_id> /bin/sh

and run the purge_app_logs command via manage.py:

python manage.py purge_app_logs <organisation_name>

By default, this will purge logs older than 30 days for all apps in the specified Organisation. You can optionally specify the number of days to retain logs, and purge logs for a specific app if required. For example, to delete logs older than 60 days for a specific App:

python manage.py purge_app_logs <organisation_name> --retain 60 --app-id <app-id>

You can find complete documentation on this utility here.


Upgrade to v2.37.2 of the Phase Console to check out these features, along with all the new features and bugfixes.

Reach out on Slack or GitHub for any questions or feedback on this release!

CLOUD

The fastest and easiest way to get started with Phase. Spin up an app in minutes. Hosted in the 🇪🇺

SELF-HOSTED

Run Phase on your own infrastructure and maintain full control. Perfect for customers with strict compliance requirements.