Deploying a Twelve-Factor App with Amazon ECS

Michael Timbs
8 min readAug 20, 2021

Every developer should be familiar with the concepts outlined in The Twelve-Factor App. The twelve-factor app is a methodology for building modern SaaS apps.

Amazon Elastic Container Service (ECS) is a great service by AWS that lets us run containerised applications with ease. By defining our Infrastructure as Code with the AWS CDK and by leveraging a service like ECS, we are able to easily build twelve-factor applications.

Let’s address the main factors of a twelve-factor app that are relevant to ECS and CDK below.

III. Config

The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy to change between deploys without changing any code; unlike config files, there is little chance of them being checked into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System Properties, they are a language- and OS-agnostic standard.

ECS lets us define the environment variables and secrets on our Container Definition. This container definition lets us define the Docker image for our container, along with any environment variables and secrets to inject into that container. An example container definition using the CDK looks something like this

applicationServiceDefinition.addContainer('app-container', {
cpu: 256,
environment: {
APP_URL: 'https://example.com',
LOG_CHANNEL: 'stdout',
LOG_LEVEL: 'debug',
DB_CONNECTION: 'mysql',
DB_HOST: db.dbInstanceEndpointAddress,
DB_PORT: db.dbInstanceEndpointPort,
CACHE_DRIVER: 'redis',
REDIS_HOST: redis.attrRedisEndpointAddress,
REDIS_PASSWORD: 'null',
REDIS_PORT: '6379',
},
image: ContainerImage.fromDockerImageAsset(applicationImage),
logging: LogDriver.awsLogs({
logGroup: applicationLogGroup,
streamPrefix: new Date().toLocaleDateString('en-ZA')
}),
memoryLimitMiB: 512,
secrets: {
DB_DATABASE: Secret.fromSecretsManager(db.secret, 'dbname'),
DB_USERNAME: Secret.fromSecretsManager(db.secret, 'username'),
DB_PASSWORD: Secret.fromSecretsManager(db.secret, 'password'),
STRIPE_KEY: Secret.fromSecretsManager(stripe, 'STRIPE_KEY'),
STRIPE_SECRET: Secret.fromSecretsManager(stripe, 'STRIPE_SECRET'),
},
});

--

--

Michael Timbs

Full-stack developer. In love with Typescript and Serverless