Loading...

Case studies

Docker and Kubernetes App Migration to Managed Container App

A practical migration guide for moving an existing Docker container, Kubernetes deployment, or mobile app backend to CDN.com.tr Managed Container App with managed Redis, managed database, object storage, scheduled jobs, domain, SSL, CDN, and WAF.

Back to Platform Help
No internal-only tool is required

The flow does not require kubectl, SSH, artisan import commands, or direct database changes. Internal tools stay on the CDN.com.tr verification and debug side only.

Panel UI flow

1

Create or select a CDN account

Sign in to CDN.com.tr first. If the customer does not already have a CDN account for this application, create one from CDN Accounts or the setup wizard, then select it before opening Platforms.

  • The setup wizard creates the CDN account shell and package relationship.
  • The Container App migration itself continues from Platforms after the account exists.
  • Use a placeholder or service domain such as api.example.com when the final domain will be connected later.
2

Confirm managed platform entitlements

Confirm that Managed Container entitlements include Container App, Redis, DB, Object Storage, persistent storage, and scheduled jobs before creating the app.

  • If Redis or DB allowance is 0, stop and assign a migration-capable package before importing data.
  • Use Standard or higher resource plans for apps that start multiple workers or initialize a large database at boot.
  • Public routing is opened through CDN edge reverse proxy only after DNS, SSL, and health checks pass.
3

Start Migration Wizard

Open the Container App creation flow from Platforms and enter the application details as an existing Docker/Kubernetes app migration. Choose an existing delivery point for the domain, or leave the custom domain empty until DNS is ready.

  • Image: registry.example.com/acme/mobile-backend
  • Tag: 1.0.0
  • Port: 8080
  • Health check: /health
4

Add registry credential

Create a read-only token from Docker Hub or the private registry, then enter it in Registry Credential. It is used only for image pull, not injected into the app as env or secret.

5

Enter env values

Enter non-secret runtime settings in KEY=VALUE format.

  • PYTHONUNBUFFERED=1
  • PYTHONPATH=/app
  • MEDIA_ROOT=/app/data/media
  • THUMBS_ROOT=/app/data/thumbs
  • IMAGE_CDN_BASE_URL=https://api.example.com
  • APP_URL=https://api.example.com
  • LOG_LEVEL=INFO
6

Enter secrets

Enter OpenAI, Meta, WhatsApp, WooCommerce, TikTok, shipping provider, and shipping sync token values in masked Secret fields. These values are not returned later in GET responses.

7

Enable Managed Redis

Enable Managed Redis. Do not enter host, port, or password; the system binds REDIS_URL to the app as env/secret.

8

Create Managed DB and import dump

Create Managed Database, then upload the .sql or .sql.gz dump from the source system. Public routing does not open until the import is complete. If the import fails, cancel the failed import record before uploading the corrected dump again.

9

Import persistent file archive

Enable persistent storage for /app/data, upload the source media/data archive as .tar.gz, and keep target mount path as /app/data. If the archive import fails, cancel the failed import record before retrying or deploying.

10

Create scheduled job

Create order-shipping-sync disabled first, run it manually, then enable the schedule after verification.

  • Schedule: */30 * * * *
  • Method: POST
  • Path: /orders/shipping-sync/run?limit=200&min_stale_minutes=120
  • Secret header: X-Order-Shipping-Sync-Token
  • Secret source: ORDER_SHIPPING_SYNC_TOKEN
11

Review, deploy, and verify

Review image, domain, Redis, DB, storage, secrets, and scheduled jobs. Start Deploy, then watch operation timeline, status, and logs.

Where credentials and import files come from

Credential sources

  • CDN.com.tr API token: create it from Authorization / API Tokens; it is visible only once.
  • Docker registry token: create a read-only token from the registry provider.
  • OpenAI key: create it from the OpenAI dashboard API keys page; rotate if the old key cannot be viewed.
  • Meta, WhatsApp, and TikTok tokens: get them from the relevant developer portal and add the application domain, such as api.example.com, as callback/domain when required.
  • WooCommerce key/secret: create it from WordPress admin, WooCommerce, Settings, Advanced, REST API.

Import files

  • DB dump: export it using mysqldump, phpMyAdmin, RDS export, or the current hosting panel.
  • Persistent archive: prepare upload/media/data folders as .tar.gz.
  • Shipping sync token: generate a new random secret; use the same value in app secret and scheduled job header.
  • Production secrets must not be written into docs, logs, or tickets.

Common migration blockers

  • Redis or DB limit exceeded means the account package is not ready for this migration; do not paste managed credentials into visible env values as a workaround.
  • HTTP 413 during DB/file import means a web/PHP upload limit is still lower than the archive size, even if the API validation allows large files.
  • Customer-facing uploads also need the CDN package file size to be large enough, because the edge config uses it as nginx client_max_body_size.
  • ErrImagePull usually means the registry credential was missing, not selected, or invalid. Select the saved read-only credential and redeploy.
  • Exit code 137 means the app likely ran out of memory. Start with a larger plan or reduce worker count for the first deploy.
  • waiting_dns means the hostname is not a ready CDN delivery point yet. Add the domain under Content Delivery Points and complete DNS/SSL before expecting public health checks to pass.
  • For S3 smoke tests, if put-object works but head-object fails, verify bucket/key ownership and the CDN S3 passthrough route.

Terminal flow with cdnctl and AWS CLI

Use cdnctl as the primary terminal workflow. Raw REST calls are only needed for API equivalence checks or troubleshooting, and AWS CLI is used only for S3-compatible Object Storage verification.

Primary cdnctl workflow

cdnctl update --check
cdnctl login --email <customer@example.com> --password <password>
cdnctl accounts list
cdnctl container registry-credentials create --account <account_uuid> --name docker --registry-url https://index.docker.io/v1/ --username <user> --password <token>
cdnctl container apps create --account <account_uuid> --name mobile-backend --image registry.example.com/acme/mobile-backend --tag 1.0.0 --port 8080 --healthcheck /health --domain api.example.com --persistent-mount-path /app/data --persistent-storage-gb 5
cdnctl object-storage buckets create --account <account_uuid> --name app-media
cdnctl object-storage access-keys create --account <account_uuid> --bucket <bucket_uuid>
cdnctl object-storage bindings create --account <account_uuid> --app <app_uuid> --bucket <bucket_uuid> --access-key <key_uuid> --env-prefix S3
cdnctl container imports database --account <account_uuid> --app <app_uuid> --file app.sql.gz
cdnctl container imports files --account <account_uuid> --app <app_uuid> --file app-data.tar.gz --target-path /app/data
cdnctl container jobs create --account <account_uuid> --app <app_uuid> --name order-shipping-sync --schedule "*/30 * * * *" --method POST --path "/orders/shipping-sync/run?limit=200&min_stale_minutes=120" --secret-header-name X-Order-Shipping-Sync-Token --secret-source ORDER_SHIPPING_SYNC_TOKEN
cdnctl container apps deploy --account <account_uuid> --app <app_uuid>
cdnctl container apps wait --account <account_uuid> --app <app_uuid> --status running --timeout 300
cdnctl container apps status --account <account_uuid> --app <app_uuid>
cdnctl container apps logs --account <account_uuid> --app <app_uuid> --tail 100

AWS CLI and public route verification

curl -fsS https://api.example.com/health
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
aws --endpoint-url https://s3.cdn.com.tr s3api list-buckets
aws --endpoint-url https://s3.cdn.com.tr s3api put-object --bucket <bucket> --key smoke.txt --body ./smoke.txt
aws --endpoint-url https://s3.cdn.com.tr s3api head-object --bucket <bucket> --key smoke.txt