Skip to main content

Atlas Kubernetes Operator v0.8: Drift Detection, Security Scanning, and Per-Resource Dev Databases

· 7 min read
Noa Rogoszinski
Noa Rogoszinski
DevRel Engineer

Hey everyone!

We just released Atlas Kubernetes Operator v0.8, which adds drift detection before and between deployments, security scanning for deployed databases, and a setting for controlling dev databases per resource:

  • Pre-Apply Drift Check: policy.drift on an AtlasMigration checks the database for drift before pending migrations are applied, and can block the deployment.
  • Scheduled Drift Checks: AtlasDriftCheck checks the database of an AtlasMigration for drift on an interval and reports the result through conditions and Kubernetes events.
  • Security Scanning: AtlasSecurityScan scans a database for extensions affected by published CVEs, on a schedule or after a schema change, and grades the findings against a policy.
  • Per-Resource Dev Database Prewarming: AtlasSchema and AtlasMigration accept prewarmDevDB, overriding the operator-wide default.

To upgrade an existing installation:

helm upgrade atlas-operator oci://ghcr.io/ariga/charts/atlas-operator --version 0.8.0

To install the operator for the first time, see Installation.

policy.drift, AtlasDriftCheck, and AtlasSecurityScan are available to Atlas Pro users. The operator authenticates with a bot token from your Atlas Cloud organization. To start a trial, run atlas login.

Drift Detection​

Drift is any change made to a database outside its migration directory. The operator now detects it in two places: right before a deployment applies pending migrations, and on a schedule between deployments.

Pre-Apply Drift Check​

The policy.drift block on an AtlasMigration enables the pre-apply drift check. Before applying pending migrations, Atlas compares the target database with the latest applied version stored in the Atlas Registry:

spec:
dir:
remote:
name: "atlas"
tag: "commit-id"
cloud:
tokenFrom:
secretKeyRef:
key: token
name: atlas-credentials
policy:
drift:
onError: FAIL # FAIL (default) or CONTINUE
exclude:
- "public.audit_*"

With onError: FAIL, a drifted database blocks the deployment before any migration file runs. The Ready condition turns False with the reason DriftDetected, and the operator retries until backoffLimit is reached:

$ kubectl get atlasmigrations
NAME READY REASON
app False DriftDetected

With onError: CONTINUE, the drift is logged and the migrations are applied. exclude takes glob patterns for objects that intentionally live outside the migration directory. The check requires a registry directory (dir.remote), since the expected state is read from the registry.

See policy.drift for the full reference, including how to recover from a blocked deployment.

Scheduled Drift Checks​

The pre-apply check runs only when a deployment has pending migrations, so a database that drifts while nothing is pending is not reported until the next deployment. AtlasDriftCheck runs atlas migrate drift on an interval, so a change made between deployments is reported when it happens. The check never modifies the database.

atlas-drift-check.yaml
apiVersion: db.atlasgo.io/v1alpha1
kind: AtlasDriftCheck
metadata:
name: myapp-drift
spec:
targetRef:
name: myapp # An AtlasMigration in the same namespace.
interval: 5m
onDrift: Report # Report (default) or Fail
exclude:
- "public.audit_*"

The check compares the database with the schema the migration directory defines at the applied version. When the target uses a registry directory, that state is read from the Atlas Registry. When it uses a ConfigMap or an inline directory, Atlas replays the migrations on the target's dev database.

The result is stored in the Drifted condition, which counts the drifted objects by kind:

Drifted=True   DriftDetected: 2 drifted objects (extra 1, modified 1) at version 20250901000000: table 2

With onDrift: Report, the resource stays Ready and drift shows up only in Drifted. With onDrift: Fail, drift also sets Ready=False, so GitOps tools that track readiness report the resource as unhealthy.

The check emits DriftDetected, DriftChanged, and DriftResolved events only when the result changes. A database that stays drifted across many checks produces a single event, so events can feed alerts directly:

kubectl get events --field-selector involvedObject.kind=AtlasDriftCheck

See Drift Detection for the full field reference.

Security Scanning​

AtlasSecurityScan runs atlas security scan from the operator. It reports the extensions installed in the database that are affected by a published CVE, resolved against the Security Graph.

security-scan.yaml
apiVersion: db.atlasgo.io/v1alpha1
kind: AtlasSecurityScan
metadata:
name: app
spec:
urlFrom:
secretKeyRef:
name: app-db
key: url
cloud:
tokenFrom:
secretKeyRef:
name: atlas-token
key: ATLAS_TOKEN
schedule: "0 6 * * *"
timeZone: UTC
triggers:
- kind: AtlasMigration
name: app-migrations
policy:
minSeverity: ELEVATED
failOn: HIGH

This scan runs once when the resource is created, every day at 06:00 UTC, and again each time the app-migrations resource applies a new version. A scan can also be requested on demand with the db.atlasgo.io/scan-requested-at annotation.

The result is split across two conditions. Ready says whether the scan ran. Compliant says whether the database is within the policy: it turns False when a finding at or above failOn is found. Use kubectl wait --for=condition=Compliant as a deployment gate:

kubectl get atlassecurityscans
NAME   READY   REASON    COMPLIANT   FINDINGS   HIGHEST   LAST SCAN   NEXT SCAN              AGE
app True Scanned False 3 HIGH 0s 2026-10-01T06:00:00Z 6s

To accept a known finding, add a waiver under policy.ignore with a reason and an optional expiration time. A waived finding stays in the report but no longer counts toward the verdict, and the scan runs again when the waiver expires:

policy:
failOn: HIGH
ignore:
- id: CVE-2026-14678
reason: "pg_trgm is not reachable from the application role; SEC-1234"
expirationTime: "2026-12-31T00:00:00Z"

Reports and Access​

The scan status holds only a summary: counts per severity level, with no extension names or CVE identifiers. The findings are stored in an AtlasSecurityReport with the same name, which lists each extension, its version, the CVEs affecting it, and a suggested fix.

Since the report shows which extensions are vulnerable, it is excluded from the built-in view and edit roles. The Helm chart ships a dedicated securityreport-viewer ClusterRole, aggregated into admin by default and configurable with rbac.securityReports.

See Security Scanning with the Kubernetes Operator for the complete guide.

Per-Resource Dev Database​

The operator keeps the dev database it manages for each resource running between reconciliations, controlled by the operator-wide prewarmDevDB Helm value. AtlasSchema and AtlasMigration now accept spec.prewarmDevDB, which overrides that default for a single resource:

apiVersion: db.atlasgo.io/v1alpha1
kind: AtlasMigration
metadata:
name: myapp
spec:
prewarmDevDB: false
urlFrom:
secretKeyRef:
key: url
name: db-credentials
dir:
configMapRef:
name: migrations

With false, the operator scales the dev database down to zero after the resource is reconciled. With true, it stays running. When the field is unset, the Helm value applies.

Wrapping Up​

Operator v0.8 checks a deployed database for drift both before and between deployments, scans it for extensions affected by published CVEs, and lets each AtlasSchema and AtlasMigration decide whether its dev database stays running. For the full changelog, see the v0.8.0 release on GitHub.

We'd love to hear your feedback! Join our Discord server or schedule a demo.