Migrate from Docker Compose to Kubernetes
Migrate a production Retool deployment from Docker Compose to a supported Kubernetes blueprints deployment.
Retool has previously supported Docker Compose for production deployments. Retool 4.0 introduces service requirements that Docker Compose cannot reliably meet in production: the agent sandbox requires resources and isolation beyond what a single-VM setup provides. Helm on Kubernetes is Retool's officially supported production deployment configuration.
This guide covers deploying a new blueprints instance, migrating your database, and cutting over DNS. Docker Compose remains supported for non-production and development use. For in-place upgrade guidance, refer to Upgrade your Docker Compose deployment.
What changed from your Docker Compose deployment
| Change | Details |
|---|---|
| Agent sandbox requires Kubernetes | The agent sandbox uses gVisor for process isolation and a custom seccomp profile. These requirements cannot be met on a single-VM Docker Compose setup. |
| Blob storage is now required | Object storage (S3, Azure Blob, or GCS) is a platform requirement for app storage, Git repository storage, and sandbox snapshots. The bundled MinIO container is only supported for development use. |
Helm values use rr.* namespace | Components associated with the new app builder are configured under rr.enabled: true in Helm values. The old Docker Compose service names (jsExecutor, r2Agent, etc.) do not map to Helm top-level keys. |
| Same-origin networking | No wildcard DNS or extra TLS certificate required. The sandbox routes through your existing Retool ingress. |
| Temporal is managed, not bundled | Retool-managed Temporal is the default. There is no temporal.yaml equivalent in the Helm chart. |
Before you start
Use this checklist as you prepare the migrate to ensure you have everything ready.
1. Collect your Docker Compose configuration
Before deploying the target instance, collect the configuration values you'll need to carry over.
Environment variables
Your Retool environment variables are stored in docker.env in your deployment directory. Open the file and note these values:
ENCRYPTION_KEY(required): must match exactly on the new deployment or your database dump will be unreadable.LICENSE_KEY.BASE_DOMAINor your Retool domain.- SSO/OIDC/SAML settings (
CLIENT_ID,CLIENT_SECRET,SSO_*,OIDC_*,SAML_*). - Any other non-default variables (SMTP, proxy settings, custom feature flags, etc.).
Scaling configuration
Note the CPU and memory limits set for each service in docker-compose.yml, and note your VM's total CPU and memory. You'll use this to size the target deployment appropriately.
Database connection details
In the default Docker Compose configuration, PostgreSQL runs as a container named postgres. If you've configured an external database, find the connection details in docker.env under POSTGRES_HOST, POSTGRES_PORT, POSTGRES_DB, and POSTGRES_USER.
2. Audit active features
Your deployment may use features that require extra steps or precautions during migration. Review these before you start:
- Workflows or Agents: disable them on the source at the start of the code-freeze window to prevent scheduled jobs from running on both instances simultaneously. Refer to step 9 for when to do this.
- Retool Database: refer to section 6 after restoring your main database.
- Retool Storage: refer to section 7 after restoring your main database.
3. Deploy the target blueprints instance
Follow the Terraform blueprints guide to stand up a new blueprints instance. Before applying Terraform, configure these migration-specific settings:
-
Set
ENCRYPTION_KEYto match your source deployment. Store it in your cloud's secrets manager and reference it viaencryption_key_secret_name. If this value doesn't match, your restored database will be unreadable. -
Set the Retool version to match or exceed your source. Pin the image tag via
retool_helm_extra_values. -
Set all custom environment variables via
retool_helm_extra_values, except SSO/OIDC/SAML settings. LeaveDISABLE_USER_PASS_LOGINunset (orfalse) so you can log in as a local admin to validate the instance before cutover. -
Match your scaling configuration. Set pod CPU, memory, and replica counts to match or exceed your source deployment.
-
Set
domain_nameto your current Retool domain. This creates a wildcard TLS certificate for it and enables a temporaryblueprints.<your-domain>subdomain for pre-cutover testing. -
If using Workflows or Agents, set
workflows_enabled = falseto prevent double-firing during the migration window.
During terraform apply, the run pauses waiting for an SSL certificate to validate. Add the DNS validation records to your DNS provider while it waits — this includes the ACM-provided CNAME record and a TXT record if your DNS provider requires domain ownership verification. The apply resumes automatically once the certificate validates. The full apply takes 30–45 minutes.
Once complete, retrieve the new load balancer hostname and add a temporary CNAME in your DNS provider:
terraform output -json modules | jq -r '.["user-ingress"].alb_dns_name'
blueprints.<your-domain> → <load-balancer-dns-name>
4. Dump the source database
How you run pg_dump depends on whether your PostgreSQL instance runs as a Docker container or as an external database.
- Containerized PostgreSQL (default)
- External PostgreSQL
Run pg_dump from within the running postgres container, then copy the dump out:
time docker exec $(docker compose ps -q postgres) pg_dump -Fc --no-owner --no-acl \
-U retool \
-d retool \
-f /tmp/retool.dump
docker cp $(docker compose ps -q postgres):/tmp/retool.dump ./retool.dump
If the container name differs in your setup, replace postgres with the service name defined in docker-compose.yml.
Run pg_dump directly from your VM if postgresql-client is installed, or via a temporary Docker container:
time docker run --rm postgres:<version> pg_dump -Fc --no-owner --no-acl \
-h <pg-host> \
-U <db-user> \
-d <db-name> \
-f - > retool.dump
Replace <version> with a PostgreSQL version equal to or lower than your source database's major version.
Common values: <db-name> is typically hammerhead_production or retool; <db-user> is typically retool or retool_admin. Confirm from docker.env.
5. Restore to the target database
The target database is in a private subnet within the Kubernetes cluster. Connect via a temporary pod.
Get the target database connection details:
terraform output -json modules | jq '{host: .["db-main"].address, db: .["db-main"].name, user: .["db-main"].username, port: .["db-main"].port}'
kubectl get secret db-credentials -n default -o jsonpath='{.data.password}' | base64 -d
Update your kubeconfig for the target cluster:
aws eks update-kubeconfig --name <cluster-name> --region <region>
Run a temporary pod with a matching PostgreSQL version:
kubectl run tmp-psql --image=postgres:<version> -it --rm --restart=Never -- sh
In a second terminal, copy the dump into the pod:
kubectl cp retool.dump tmp-psql:/retool.dump
Inside the pod, set connection variables and restore:
DROP DATABASE … WITH (FORCE) requires PostgreSQL 13 or later. On PostgreSQL 12 or earlier, use SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'retool'; before dropping.
export PGHOST=<host> PGPORT=<port> PGUSER=<user> PGPASSWORD=<password>
# Drop and recreate the database
psql -d postgres -c "DROP DATABASE retool WITH (FORCE)" -c "CREATE DATABASE retool"
# Restore
time pg_restore --no-owner --no-acl -d retool /retool.dump
# Restart all deployments to reconnect pods and run any pending migrations
kubectl rollout restart deployment -n default
6. Migrate Retool Database
If your deployment does not use Retool Database, skip this section.
Retool Database is a separate PostgreSQL database from the main Retool application database. You must dump and restore it separately, then update the connection strings on the new deployment.
Dump the Retool Database from within the postgres container (or from your external database host):
time docker exec $(docker compose ps -q postgres) pg_dump -Fc --no-owner --no-acl \
-U retool \
-d <retooldb-name> \
-f /tmp/retooldb.dump
docker cp $(docker compose ps -q postgres):/tmp/retooldb.dump ./retooldb.dump
Copy the dump into a temporary pod in the target cluster and restore it, following the same process as section 5:
psql -d postgres -c "CREATE DATABASE retooldb"
time pg_restore --no-owner --no-acl -d retooldb /retooldb.dump
After restoring, update the Retool Database connection string in retool_helm_extra_values to point to the new database.
7. Migrate Retool Storage
If your deployment does not use Retool Storage, skip this section.
Retool Storage uses blob storage. The default Docker Compose configuration includes a bundled MinIO container, which stores blobs on the local VM disk. Before cutover, migrate this data to a durable cloud bucket.
- Bundled MinIO
- External cloud bucket
Copy data from the MinIO container to your cloud storage bucket. First, identify the MinIO container name from docker-compose.yml, then use the MinIO CLI or aws s3 sync with a MinIO-compatible endpoint:
aws s3 sync s3://<minio-bucket> s3://<target-bucket> \
--endpoint-url http://localhost:9000 \
--profile minio
After syncing, update retool_helm_extra_values to point Retool Storage at the target bucket.
If your Docker Compose deployment already uses an external cloud bucket, you have two options:
-
Point the new deployment at the same bucket. The VM role or credentials that previously accessed the bucket are a different IAM principal from the EKS node or pod role. You must grant the new principal access before cutover. For AWS S3, add the EKS node IAM role to the bucket policy or use IRSA. Refer to the S3 and IAM reference configuration in the blueprints repo.
Set the storage environment variables in
retool_helm_extra_valuesto reference your existing bucket before cutover. -
Copy blobs to a new bucket and update the environment variables to point at it:
Sync blobs to new bucketaws s3 sync s3://<source-bucket> s3://<target-bucket>
8. Validate
Once the restore is complete, log in to blueprints.<your-domain> as a local admin and verify:
- Encryption: go to Resources and open any resource. If the credentials are readable, your encryption key is correct. If fields show garbled or empty values, the
ENCRYPTION_KEYon the new instance doesn't match the source. - Functional parity: spot check critical apps and queries. Confirm users, permissions, and audit logs look correct. Workflows will be non-functional here since you disabled them.
If anything looks wrong, you can repeat the dump and restore without affecting the source deployment. Nothing done so far has modified your Docker Compose instance.
9. Cut over DNS
This is the live migration. Time it during a low-traffic window and communicate the maintenance period to users in advance.
-
Designate a code-freeze window. The final database dump is a point-in-time snapshot. Any apps, workflows, or config changes made after the final dump will not carry over.
-
Begin the freeze and notify users.
-
If using Workflows or Agents, disable them on the source to prevent jobs from double-firing after the new instance comes up.
-
Take the final database dump by repeating sections 4 and 5 (and sections 6 and 7 if applicable).
-
Validate on
blueprints.<your-domain>one more time. -
Cut over DNS. Update your DNS provider to point
<your-domain>at the new load balancer. The blueprints Terraform creates a DNS zone with the correct records. The cleanest path is to delegate your domain to it by updating the NS record at your registrar:Get nameserversterraform output -json modules | jq -r '.["user-ingress"].zone_name_servers' -
Verify the cutover. Once DNS propagates, confirm traffic is reaching the new instance:
Verify DNS propagationdig +short <your-domain>Verify SSO is working end-to-end.
-
Re-enable Workflows. Set
workflows_enabled = trueand runterraform apply. Verify scheduled jobs are running on the new instance. -
End the code freeze and notify users.
Post-migration
Leave the Docker Compose deployment stopped but intact for at least a week. Don't decommission it until you're confident the new instance is stable. To roll back, point DNS back at the original load balancer and re-enable Workflows on the source.
Once the new deployment is confirmed healthy, shut down the Docker Compose stack and decommission the VM.