Migrating from AWS Elastic Beanstalk
This guide is for teams running web and worker tiers on AWS Elastic Beanstalk who want a faster multi-service deploy workflow without leaving AWS. You stay on the same AWS account. Convox installs an EKS-based Rack into that account, and you describe your whole application, multiple services, scheduled jobs, and datastores, in a single convox.yml. Where Beanstalk gives you one application version per environment and a separate worker environment for background jobs, Convox runs every service, timer, and resource for an app from one manifest and one convox deploy.
Prerequisites
- A Convox Rack installed into your AWS account. See AWS installation.
- The
convoxCLI installed and logged in. - A
Dockerfilefor each service. Elastic Beanstalk's Docker platform builds from aDockerfile(or pulls a prebuilt image referenced inDockerrun.aws.json), so most projects already have one. If your application ran on a non-Docker Beanstalk platform (for example the native Python, Node.js, or Ruby platforms), you will need to add aDockerfile. See Dockerfile.
Concept Mapping
| Elastic Beanstalk | Convox |
|---|---|
| Application | App |
| Environment (web tier) | A Rack plus a web Service with a port |
| Worker environment (SQS daemon tier) | A worker Service (no port) |
Dockerrun.aws.json / Docker platform |
Dockerfile plus a service entry in convox.yml |
Multi-container Dockerrun.aws.json v2 |
Multiple services in one convox.yml |
| Environment properties | Environment variables |
.ebextensions/*.config |
convox.yml plus rack parameters |
cron.yaml periodic tasks |
Timers |
| RDS attached to an environment | A Resource (rds-postgres, etc.) or an external/imported RDS |
| ElastiCache | An elasticache-redis / elasticache-memcached Resource |
| Health check URL | Service health path |
eb deploy |
convox deploy |
| Rolling deployments | Rolling updates (default) |
convox.yml
The example below covers a common Beanstalk setup: a web tier serving HTTP and a separate worker environment that drains an SQS queue, with a cron.yaml periodic task.
Before (Elastic Beanstalk)
A multi-container web environment defined with Dockerrun.aws.json v2:
{
"AWSEBDockerrunVersion": 2,
"containerDefinitions": [
{
"name": "web",
"image": "myorg/web:latest",
"essential": true,
"memory": 512,
"portMappings": [
{ "hostPort": 80, "containerPort": 3000 }
]
}
]
}
Environment properties were set on the environment (console, eb setenv, or an .ebextensions option_settings block under aws:elasticbeanstalk:application:environment).
A separate worker environment ran the background processor, and its cron.yaml queued a periodic task:
version: 1
cron:
- name: "nightly-cleanup"
url: "/tasks/cleanup"
schedule: "0 3 * * *"
After (convox.yml)
One manifest describes the web service, the worker service, and the scheduled job:
environment:
- DATABASE_POOL=10
services:
web:
build: .
command: bin/web
port: 3000
health: /health
environment:
- SECRET_KEY_BASE
resources:
- database
worker:
build: .
command: bin/worker
environment:
- SECRET_KEY_BASE
resources:
- database
resources:
database:
type: postgres
timers:
nightly-cleanup:
schedule: "0 3 * * *"
command: bin/cleanup
service: worker
Notes:
portmakeswebreachable through the rack's load balancer. A service with noport, likeworker, runs as a background process and is not exposed externally, which is the equivalent of a Beanstalk worker tier.healthreplaces the Beanstalk health check URL. See Health Checks.- The
commandoverrides the imageCMD, so the sameDockerfilecan back bothwebandworker. If your worker uses a different build, point itsbuildat another directory.
Environment and Secrets
Beanstalk environment properties map directly to Convox environment variables. Declare the variable names in convox.yml and set their values with the CLI:
$ convox env set SECRET_KEY_BASE=$(cat secret) DATABASE_POOL=10 -a myapp
Setting DATABASE_POOL, SECRET_KEY_BASE... OK
Release: RABCDEFGHI
Setting environment variables creates a new Release. Promote it to apply the change:
$ convox releases promote RABCDEFGHI -a myapp
Differences worth noting:
- A variable declared at the top level of
convox.ymlis available to every service, like an environment-wide property. A variable declared under a single service is scoped to that service only. - You can set a default in the manifest (
DATABASE_POOL=10); a bare name (SECRET_KEY_BASE) must be set withconvox env setbefore the release will promote. - Convox stores values as Kubernetes Secrets in your cluster. There is no separate "save and apply" step in a console; the change rides along with the release.
Datastores
If your Beanstalk environment provisioned an RDS instance, you have two clean paths.
Let Convox manage a new database
Define a managed RDS Resource and link it to the services that need it. Convox provisions the RDS instance in your AWS account and injects a connection URL:
resources:
database:
type: rds-postgres
options:
class: db.t3.medium
storage: 100
version: "16"
encrypted: true
durable: true
services:
web:
resources:
- database
Linking injects DATABASE_URL (and the broken-out DATABASE_HOST, DATABASE_USER, etc.) into the service. See the Resource reference for the full variable list and for containerized resources you can use in development.
Keep your existing RDS instance
If you would rather keep the database that Beanstalk created, you have two options:
- Import it so Convox manages it going forward, using the
importoption withmasterUserPassword. See Database Import. - Point at it directly by setting the resource's connection environment variable yourself. Setting
DATABASE_URLon the app stops Convox from starting a containerizeddatabaseresource and connects the service straight to your existing RDS endpoint. See Overlays.
$ convox env set DATABASE_URL=postgres://user:pass@my-db.abc123.us-east-1.rds.amazonaws.com:5432/app -a myapp
ElastiCache maps the same way using the elasticache-redis and elasticache-memcached resource types. Convox Cloud also offers managed databases if you do not want to manage RDS yourself.
Scheduled Jobs and Workers
Beanstalk splits background work into a separate worker environment with an SQS daemon, where periodic tasks are declared in cron.yaml and delivered as HTTP POSTs to a local endpoint. Convox handles the two responsibilities separately and more directly:
- Continuous background processing (the SQS daemon equivalent) is a worker Service: a service with no
portthat runs your queue consumer as a long-lived process. It lives in the same app and manifest as the web service. - Periodic, scheduled tasks (the
cron.yamlequivalent) are Timers. A timer runs acommandon ascheduleagainst a namedservice:
timers:
nightly-cleanup:
schedule: "0 3 * * *"
command: bin/cleanup
service: worker
A timer spawns a fresh Process of the named service for each run, so you do not need a queue, a daemon, or an HTTP endpoint to trigger scheduled work. Timer schedules use standard cron syntax and run in UTC. If a timer should run against a service that is otherwise idle, scale that service to zero and use it as a template; see Using a Template Service.
If your background work scales with queue depth rather than a fixed schedule, Convox can autoscale a worker service on a queue metric. See Autoscaling.
Deploy and Cutover
-
Add a
convox.yml(and aDockerfileif you did not have one) to your repository. -
Create the app on the rack:
$ convox apps create myapp -
Set the environment variables your services require:
$ convox env set SECRET_KEY_BASE=... DATABASE_POOL=10 -a myapp -
Deploy. This builds your images, creates a Release, and promotes it:
$ convox deploy -a myapp -
Find the rack-generated hostname for the web service and test it before changing any DNS:
$ convox services -a myapp SERVICE DOMAIN PORTS web web.myapp.0a1b2c3d.convox.cloud 443:3000Exercise the app against that hostname (and migrate or restore your database if needed) while Beanstalk continues to serve production traffic.
-
Cut over DNS last. Once the Convox-hosted app is verified, point your domain at the Convox web service and attach your custom domain. See Custom Domains and SSL. Because you only move DNS at the end, you can roll back by pointing DNS back at Beanstalk if anything looks wrong.
-
After traffic has shifted and you are confident, terminate the Beanstalk environments to stop paying for them.
To minimize downtime, keep both stacks running in parallel until DNS has fully propagated, and use a short TTL on the DNS record before the cutover so the switch takes effect quickly.
Gotchas and Differences
- No worker-tier SQS daemon. Beanstalk's worker tier converts queue messages into HTTP POSTs to
localhost. Convox does not. Run your existing queue consumer as a worker service that reads SQS directly, and use Timers for anything you previously expressed incron.yaml. Thecron.yamlHTTP-POST pattern has no direct equivalent; the task logic moves into the timer'scommand. .ebextensionsdoes not carry over. Platform and instance tuning that lived in.ebextensionsis split in Convox: application shape goes inconvox.yml, and cluster/platform settings (instance types, scaling, logging, networking) are rack parameters. There is no per-deploy shell-hook mechanism; use a serviceinitContainerfor migrations or setup that must run before the app starts.- Static
scale.countis first-deploy only. Unlike a Beanstalk environment where you adjust capacity in the console, acountinconvox.ymlis applied only on the first deploy. After that, change scale withconvox scaleor configure autoscaling. - One app, many services. You do not create a separate environment per tier. Web and worker live in the same app and deploy together with one
convox deploy. If you previously kept entirely separate Beanstalk applications, you can keep them separate as distinct Convox apps. - The load balancer is shared and managed. Convox provisions and manages the rack load balancer for you. Service routing is driven by the
portfield rather than by a per-environment ELB you configure directly. - Logs and shell access. Use
convox logs -a myappfor application logs andconvox exec/convox runinstead ofeb logsandeb ssh.
See Also
- convox.yml for the full manifest reference
- Service for service configuration including health checks and scale
- Timer for scheduled jobs
- Resource for managed and containerized datastores
- Environment Variables for configuration and secrets
- Deploying Changes for the build and promote workflow
- AWS installation for installing a Rack into your AWS account