Skip to main content

Manage your database schema with Atlas using Crossplane

This guide walks you through managing your database schema with Atlas on Kubernetes using Crossplane, a powerful control plane framework for managing infrastructure as code.

Why Crossplane?

Using Crossplane to manage the Atlas Operator provides several advantages:

  1. Infrastructure as Code: All resources are defined declaratively in YAML
  2. Version Control: Track changes to your operator configuration in Git
  3. Composition: Combine multiple resources into reusable templates
  4. Multi-Cluster: Use Crossplane's composition features to deploy across multiple clusters
  5. Unified API: Manage databases, operators, and applications using the same Kubernetes API

For more information about Crossplane, visit the official documentation.

Prerequisites

Before you begin, ensure you have:

  • A Kubernetes cluster (this guide uses minikube)
  • kubectl installed and configured
  • Crossplane installed on your cluster

Step 1: Install Crossplane Providers

note

If you already have the Crossplane Helm and Kubernetes providers installed, you can skip this step and proceed directly to Step 2.

Crossplane uses providers to interact with different APIs. For Atlas Operator, you'll need:

  • Helm Provider: To install the operator as a Helm chart
  • Kubernetes Provider: To create AtlasSchema and AtlasMigration resources

Install the Helm Provider

Create a file named provider-helm.yaml:

apiVersion: pkg.crossplane.io/v1beta1
kind: DeploymentRuntimeConfig
metadata:
name: helm-runtime-config
spec:
serviceAccountTemplate:
metadata:
name: provider-helm-sa
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-helm
spec:
package: xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0
runtimeConfigRef:
name: helm-runtime-config
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: provider-helm-sa
namespace: crossplane-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: provider-helm-admin-binding
subjects:
- kind: ServiceAccount
name: provider-helm-sa
namespace: crossplane-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

Apply it:

kubectl apply -f provider-helm.yaml

Configure the Helm provider with provider-helm-config.yaml:

apiVersion: helm.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: default
spec:
credentials:
source: InjectedIdentity

Apply it:

kubectl apply -f provider-helm-config.yaml

Install the Kubernetes Provider

Create a file named provider-k8s.yaml:

apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-kubernetes
spec:
package: xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0

Apply it:

kubectl apply -f provider-k8s.yaml

Configure the Kubernetes provider with provider-k8s-config.yaml:

apiVersion: kubernetes.m.crossplane.io/v1alpha1
kind: ProviderConfig
metadata:
name: provider-kubernetes
spec:
credentials:
source: InjectedIdentity

Apply it:

kubectl apply -f provider-k8s-config.yaml

Verify Provider Installation

Wait for both providers to become healthy:

kubectl get providers.pkg.crossplane.io

You should see output similar to:

NAME                  INSTALLED   HEALTHY   PACKAGE
provider-helm True True xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0
provider-kubernetes True True xpkg.crossplane.io/crossplane-contrib/provider-kubernetes:v1.0.0

Step 2: Install Atlas Operator via Crossplane

Now that the providers are ready, create a Helm Release resource to install the Atlas Operator.

Create a file named atlas-operator-release.yaml:

apiVersion: helm.crossplane.io/v1beta1
kind: Release
metadata:
name: crossplane-atlas-operator
spec:
forProvider:
chart:
name: atlas-operator
repository: oci://ghcr.io/ariga/charts
namespace: atlas-operator-system
providerConfigRef:
name: default

Apply it:

kubectl apply -f atlas-operator-release.yaml

Verify the Installation

Check the Helm release status:

kubectl get release.helm.crossplane.io crossplane-atlas-operator

You should see:

NAME                        CHART            VERSION   SYNCED   READY   STATE      REVISION
crossplane-atlas-operator atlas-operator 0.7.16 True True deployed 1

Verify the operator pod is running:

kubectl get pods -n atlas-operator-system

Expected output:

NAME                                         READY   STATUS    RESTARTS   AGE
crossplane-atlas-operator-64d5d6887b-xxxxx 1/1 Running 0 30s

Check that the Atlas CRDs are installed:

kubectl get crd | grep atlas

You should see:

atlasmigrations.db.atlasgo.io
atlasschemas.db.atlasgo.io

Configure RBAC for Kubernetes Provider

Before creating AtlasSchema resources, you need to grant the Kubernetes provider permission to manage them.

First, get the service account name used by the Kubernetes provider:

kubectl get sa -n crossplane-system | grep provider-kubernetes

Create a file named k8s-provider-rbac.yaml, replacing <SA_NAME> with the actual service account name from the previous command:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: atlas-schema-manager
rules:
- apiGroups: ['db.atlasgo.io']
resources: ['atlasschemas', 'atlasmigrations']
verbs: ['*']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: provider-kubernetes-atlas-binding
subjects:
- kind: ServiceAccount
name: <SA_NAME>
namespace: crossplane-system
roleRef:
kind: ClusterRole
name: atlas-schema-manager
apiGroup: rbac.authorization.k8s.io

Apply it:

kubectl apply -f k8s-provider-rbac.yaml

Step 3: Create an AtlasSchema Resource

With the operator running, you can now create AtlasSchema resources using Crossplane's Kubernetes provider.

An AtlasSchema resource represents the desired state of your database schema. When you create one, you define the schema you want (using SQL or HCL), and the Atlas Operator ensures your database matches that definition. The operator automatically calculates the diff between your desired schema and the current database state, then applies the necessary migrations.

Since we're using Crossplane, we wrap the AtlasSchema in a Crossplane Object resource. This allows Crossplane to manage the lifecycle of the AtlasSchema and provides additional benefits like drift detection and reconciliation.

First, create database credentials. For this example, we'll use PostgreSQL:

kubectl create secret generic postgres-credentials \
--from-literal=url="postgres://postgres:pass@postgres.default:5432/postgres?sslmode=disable&search_path=public"
note

Replace the connection string with your actual database credentials.

Create a file named atlas-schema.yaml:

apiVersion: kubernetes.m.crossplane.io/v1alpha1
kind: Object
metadata:
name: atlas-schema-pg
namespace: default
spec:
forProvider:
manifest:
apiVersion: db.atlasgo.io/v1alpha1
kind: AtlasSchema
metadata:
name: atlas-schema-pg
namespace: default
spec:
urlFrom:
secretKeyRef:
key: url
name: postgres-credentials
schema:
sql: |
create table t1 (
id int
);
providerConfigRef:
kind: ProviderConfig
name: provider-kubernetes

Apply it:

kubectl apply -f atlas-schema.yaml

Once applied, the Atlas Operator detects the new AtlasSchema resource and begins reconciling. It connects to your database using the provided credentials, inspects the current schema, and applies the necessary changes to reach the desired state defined in the schema.sql field. In this case, it will create the t1 table with an id column.

Verify the Schema Application

Check the Crossplane Object status:

kubectl get object.kubernetes.m.crossplane.io atlas-schema-pg

Expected output:

NAME              KIND          PROVIDERCONFIG        SYNCED   READY   AGE
atlas-schema-pg AtlasSchema provider-kubernetes True True 30s

Check the AtlasSchema resource directly:

kubectl get atlasschema atlas-schema-pg

You should see:

NAMESPACE   NAME              READY   REASON
default atlas-schema-pg True Applied

Get detailed status:

kubectl get atlasschema atlas-schema-pg -o jsonpath='{.status.conditions[?(@.type=="Ready")].message}'

This will show the apply response with details about the schema changes.

Next Steps

Now that you have the Atlas Operator running via Crossplane, you can: