> ## Documentation Index
> Fetch the complete documentation index at: https://docs.localops.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Database Migration

In this guide, we demonstrate how DB migrations can be integrated into Helm charts in such a way that satisfies the
principles for deploying schema migrations before starting the application. We will see how to handle both DB schema and
data migrations.

<Note>
  We recommend using a robust migration framework for your applications that uses versioned migration files. Most migration frameworks support linear migrations, which only allow progressive changes.

  Therefore, if you need to remove a database column or field introduced in an earlier migration, you must create a new
  migration file specifically for this purpose. This approach ensures that all changes are tracked and applied in a
  controlled and sequential manner.
</Note>

## Prerequisites

1. A migrations docker image
2. A Helm chart defining your application.

## Schema Migrations

### Using Helm lifecycle hooks

To satisfy the principle of having migrations run before the new application version starts, as well as ensure that
**only one migration job runs concurrently**, we use Helm's `pre-install` and `pre-upgrade` hooks feature.

<Info>
  Helm pre-upgrade hooks are chart hooks that:

  Executes on an upgrade request after templates are rendered, but before any resources are updated
</Info>

To use `pre-install` and `pre-upgrade` hooks to run migrations as part of our chart definition, we create a template for
a **Kubernetes Job** and annotate it with the relevant Helm hook annotations.

<Tabs>
  <Tab title="General">
    ```yaml job.yaml theme={null}
      apiVersion: batch/v1
      kind: Job
      metadata:
        # Job name should include a unix timestamp to make sure it's unique
        name: "{{ .Release.Name }}-migrate-{{ now | unixEpoch }}"
        labels:
          helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
        annotations:
          "helm.sh/hook": pre-install,pre-upgrade
          "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
      spec:
        template:
          metadata:
            name: "{{ .Release.Name }}-db-migrate"
            labels:
              app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
              app.kubernetes.io/instance: {{ .Release.Name | quote }}
              helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
          spec:
            restartPolicy: Never
            imagePullSecrets:
              - name: {{ .Values.imagePullSecret }}
            containers:
              - name: db-migrate
                # Image to run DB migration
                image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
                # Command to run DB migration
                args:
                  - migrate
                  - apply
                  - -u
                  - {{ .Values.dburl }}
                  - --dir
                  - file:///src/
                # Environment variables required for migrations to run
                env:
                  - name: DATABASE_HOST
                    value: {{ .Values.django.env.DATABASE_HOST }}
                  - name: DATABASE_PORT
                    value: {{ .Values.django.env.DATABASE_PORT }}
                  - name: DATABASE_NAME
                    value: {{ .Values.django.env.DATABASE_NAME }}
                  - name: DATABASE_USER
                    value: {{ .Values.django.env.DATABASE_USER }}
                  - name: DATABASE_PASSWORD
                    value: {{ .Values.django.env.DATABASE_PASSWORD }}
    ```
  </Tab>

  <Tab title="Django">
    ```yaml job.yaml theme={null}
      apiVersion: batch/v1
      kind: Job
      metadata:
        # Job name should include a unix timestamp to make sure it's unique
        name: "{{ .Release.Name }}-migrate-{{ now | unixEpoch }}"
        labels:
          helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
        annotations:
          "helm.sh/hook": pre-install,pre-upgrade
          "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
      spec:
        template:
          metadata:
            name: "{{ .Release.Name }}-db-migrate"
            labels:
              app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
              app.kubernetes.io/instance: {{ .Release.Name | quote }}
              helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
          spec:
            restartPolicy: Never
            imagePullSecrets:
              - name: {{ .Values.imagePullSecret }}
            containers:
              - name: migrate
                # Image to run migration
                # Incase of Python Django, it is the same image as application
                image: {{ .Values.django.image.repository }}:{{ .Values.django.image.tag }}
                # Command to run DB migration
                command: ["sh", "-c", "python manage.py migrate"]
                # Environment variables required for migrations to run
                env:
                  - name: DATABASE_HOST
                    value: {{ .Values.django.env.DATABASE_HOST }}
                  - name: DATABASE_PORT
                    value: {{ .Values.django.env.DATABASE_PORT }}
                  - name: DATABASE_NAME
                    value: {{ .Values.django.env.DATABASE_NAME }}
                  - name: DATABASE_USER
                    value: {{ .Values.django.env.DATABASE_USER }}
                  - name: DATABASE_PASSWORD
                    value: {{ .Values.django.env.DATABASE_PASSWORD }}
    ```
  </Tab>
</Tabs>

<Info>
  Above Job will be run each time when an [Environment] is created and when a deployment is made with a new helm chart
  version.
</Info>

## Data Migrations

Similarly, DB data migrations can be performed within the same migration files and executed sequentially in a consistent
order. This approach allows you to manage both schema and data changes together within the same migration framework,
ensuring consistency and ease of development and maintenance.

<Tip>
  LocalOps intends to adopt Blue/Green deployment strategy soon. Therefore, it is advisable to avoid introducing
  breaking database or API changes in consecutive deployments. This practice ensures smoother transitions and minimizes
  disruptions during version upgrades.
</Tip>

**In this article you have learnt how to run DB migrations from your Helm Chart inside [Environment].** 👍🏽

[Environment]: https://console.localops.co/environments
