Deploy Your Django Apps in AWS With CDK (v2) | by Mariano Martinez Grasso | Apr, 2022

Picture by Darya Jum on Unsplash

Infrastructure as code (IaC) means provisioning and managing your infrastructure by “code” as a substitute of doing it manually. An ideal benefit of this code is that now you may handle it as you do together with your software program code. You possibly can commit it to some repository, you may evaluate and evaluate modifications, you are able to do rollbacks, and you may reuse the code in different tasks.

However this “code” sometimes means writing YAML or JSON configuration information in their very own language, and these languages are sometimes not as developer-friendly as chances are you’ll count on.

For instance, let’s verify the code of this CloudFormation Template (An IaC config file in AWS) used to create a load-balanced Fargate service working a minimal flask app:

The language is generally declarative; the syntax and construction are advanced, and you must take care of quite a lot of low-level configurations associated to networking and permissions. Additionally, because the structure complexity grows, the variety of strains of code grows quick. All this makes sustaining these information painful.

The Cloud Development Kit (CDK) brings IaC to the following degree by supporting fashionable programming languages like Python, TypeScript, and JavaScript. This lets you mannequin your infrastructure by doing Object-Oriented Programming (OOP) by writing lessons and instantiating objects. On the similar time, the CDK libraries supply higher-level abstractions which may encapsulate particulars associated to networking and permissions selecting good (however editable) defaults.

For instance, let’s see the code required to create a load-balanced Fargate service with CDK in Python:

We will see that it’s simply OOP in Python, and the variety of strains of code was drastically diminished. Now you may keep your infrastructure code identical to you keep your software code. You possibly can apply greatest practices and even add unit tests for it!

How does CDK work?

Diagram by Amazon on cdk docs

CDK is constructed on high of CloudFormation. It takes the Python code and synthesizes CloudFormation templates from it. It comes with a toolkit that permits creating a brand new CDK challenge, synthesizing the code, and deploying it utilizing a selected account and area.

CDK fundamentals

  • Constructs: A construct represents a cloud useful resource in AWS, e.g., an S3 bucket, a load balancer, or an RDS database. You sometimes use them as lower-level constructing blocks of your structure.
  • Resolution Constructs: A solution construct is a assemble with a better degree of abstraction that implements frequent structure patterns, e.g., a load-balanced ECS+Fargate service.
  • Stacks: A stack teams sources that are supposed to be deployed collectively, e.g., an S3 bucket plus a CloudFront distribution which can be used collectively to serve some static information, a load-balanced Fargate service plus autoscaling insurance policies which can be used to serve your Django app.
  • Stage: A stage can group stacks to mannequin a single occasion of the entire system. A stage will be instantiated a number of occasions to deploy your software in a number of environments corresponding to testing, staging, or manufacturing.
  • App: The app is the basis assemble that represents the scope of your entire CDK challenge. A single app is instantiated per a CDK challenge and stacks or levels are added as little one nodes.
  • AWS Setting: An Environment is a mixture of an account and a area the place a stack goes to be deployed. We are going to set the AWS surroundings once we instantiate a Stack.


That is what it is advisable begin CDK:

Additionally, Dockerize your Django App. The chosen structure requires a containerized software

The code

You’ll find the complete code of this pattern challenge on GitHub.

Our structure within the cloud

We are going to run our Django app as Serverless Containers utilizing ECS and Fargate. And we may also add queues and staff utilizing SQS and Celery to allow the execution of long-running duties within the background. If you wish to know extra in regards to the options of this particular structure please learn this earlier article: Design an auto-scalable Architecture for your Django Apps in AWS.

You may get this diagram in full-size here

Defining the stacks

It’s an excellent follow to outline separate stacks for parts having completely different life cycles, and particularly to maintain stateful and stateless parts aside. For instance, an ECS+Fargate service working the Django app is stateless and you’ll most likely replace, destroy or recreate these sources extra usually (e.g., with new releases or on scaling occasions).

However a database is stateful. You most likely don’t wish to destroy it or change it. Normally, its life cycle shall be decoupled from the appliance life cycle.

So, we are going to divide our infrastructure code into the next stacks:

You may get this diagram in full-size here

Let’s begin with the enjoyable half!

Step one is to make a listing after which use the CDK consumer to initialize a template challenge. We select the Python language. The listing title shall be used because the challenge title, which shall be my-django-app this time.

$ mkdir my-django-app
$ cd my-django-app/
$ cdk init app --language python
Making use of challenge template app for python
...Initializing a brand new git repository...
Please run 'python3 -m venv .venv'!
Executing Creating virtualenv...
✅ All completed!

Let’s see the challenge construction:

A CDK challenge preliminary construction
  • venv: CDK creates a base challenge and a virtualenv for you. Discover that this digital surroundings is simply meant for use whereas working within the infrastructure code and it should not be used as your software digital env. Keep away from putting in software dependencies right here.
  • That is the entry level for the CDK challenge the place the basis app assemble and your foremost stacks are instantiated.
  • necessities.txt: These are the core Python dependencies required to work with CDK, together with the CDK core library itself. You may also add third-party packages containing further constructs or resolution constructs right here.
  • requirements-dev.txt: These are different dependencies required throughout growth, for instance, to run unit exams on CDK stacks.
  • my_django_app/ CDK creates a folder in your stacks and one stack in your app by default. We are going to add all our stacks inside this listing.

Now we are going to activate the virtualenv and set up dependencies:

$ supply .venv/bin/activate
(.venv) $ pip set up -r necessities.txt
(.venv) $ pip set up -r requirements-dev.txt


As we’re going to generate belongings like docker photographs through the deployment, CDK requires bootstrapping. First, be sure to have configured the AWS consumer beforehand or evaluate the Conditions part. Now all it is advisable do is run the bootstrap command together with your account id and area title:

$ cdk bootstrap aws://123456789123/us-east-2
⏳ Bootstrapping surroundings aws://123456789123/us-east-2...
✅ Setting aws://123456789123/us-east-2 bootstrapped

For every stack, we are going to add a brand new file within the stacks listing my_django_app/, and we are going to outline a brand new Stack subclass.

The Community Stack

We are going to begin creating our Virtual Private Cloud (VPC), some VPC endpoints, and an ECS Cluster for our companies. Our purpose is to create these sources as soon as and to share them with different Stacks to help our structure, so we group them in an impartial Stack.


The NetworkStack class inherits from the CDK Stack class. Then we outline our sources within the __init__ technique. First, we use the assemble Vpc to create the digital personal community.

All of the constructs obtain two first necessary arguments: scopeand id . scopeis a reference to a mum or dad assemble, and id is an identification string that should be distinctive throughout the scope. In our stack, we now have renamed the id argument to construct_id as a result of id is a reserved phrase in Python.

Then, persevering with with the VPC parameters, we select to have two Availability Zones (AZ), as that’s the minimal required to get excessive availability and nil NAT gateways to scale back prices. This can generate one public and one personal remoted subnet per AZ.

We additionally have to allow the DNS help and DNS hostnames because it’s required to make use of VPC endpoints. We additionally create a cluster within the Amazon Elastic Container Service (ECS). An ECS cluster is a logical grouping for ECS companies the place the Django app and the employees shall be positioned.

Then we add the VPC endpoints for all of the managed companies being utilized in our design, which in any other case would require web outgoing site visitors (after which NAT gateways):

  • S3
  • ECR
  • CloudWatch Logs
  • Secrets and techniques Supervisor
  • SQS

Lastly, as CDK generates random names through the creation of the sources, it’s helpful to avoid wasting parameters to seek out them or reference them later. On this case, we are going to save the VPC id and the subnets ids as parameters within the System Manager (SSM) Parameter Store. We are going to use a naming conference for all of the parameters and secrets and techniques with the next kind:


So, for instance, the complete title of the VPC id parameter for the staging surroundings shall be: /MyDjangoAppStaging/VpcId.


To create the database, we use the ServerlessCluster assemble which helps completely different database engines. On this case, we select the Aurora PostgreSQL engine with PostgreSQL model 10 (which is the newest supported model on the time of writing this text).

We additionally set subnets and permissions to make the database accessible solely from personal subnets (the place our Django app will reside).

Additionally, this time we are going to add some further parameters within the __init__ technique so we will customise some settings of this stack when instantiating it for various environments:

  • VPC: It’s the VPC the place the database will reside. This would be the VPC that we created earlier within the community stack.
  • database_name: This parameter permits us to set the title of the default database, which shall be utilized by our Django app later. The ServerlessCluster assemble creates a default database with the title given within the default_database_name parameter.
  • min_capacity and max_capacity: These parameters outline scaling limits for the cluster. The computing energy is measured in “Aurora Capacity Units” (ACUs) that are a mixture of CPU/Reminiscence/Community capability. The minimal is 2 ACUs, and the utmost is 384. Then the database will auto-scale primarily based on a mixture of metrics on CPU utilization, connections, and out there reminiscence, however at all times throughout the outlined limits.
  • auto_pause_minutes: This can permit us to close down the computing energy of the database when it’s IDLE for a certain quantity of minutes, which means scaling all the way down to zero capability. That is one thing chances are you’ll wish to do in a testing / staging surroundings to save lots of prices. The database capability shall be restored the following time a brand new connection is detected. It’s best to know that there’s a warm-up time which will be round one minute.
  • backup_retention_days: Database backups are mechanically deleted after this variety of days. Aurora does automatic backups as soon as a day and retaining backups for sooner or later is free.

After the database is created by CDK, a brand new secret is added in Secrets and techniques Supervisor containing the database cluster URL, the database title, and the database credentials. As we might want to discover this secret later, we are going to save the title of the key within the SSM Parameter retailer.

The static information stack

We’re going to retailer the static information in an S3 bucket, and we are going to serve them by a CloudFront Distribution.


First, we create a personal bucket. For safety causes, we don’t need the bucket to be public, so we block any site visitors coming from the web. Because the static information will be re-generated anytime, we permit CDK to destroy the bucket and its objects as wanted.

Then we create an Origin Access Identity (OAI) to permit our CloudFront distribution to entry the information within the bucket.

Additionally if cors_allowed_origins is supplied, we add a CORS Coverage to permit requests coming from these area/subdomains solely. Else all of the origins are allowed.

Lastly, this time we are going to save the s3 bucket title and the CloudFront distribution URL within the SSM Parameter Store.

The exterior secrets and techniques stack

There are some parameters that we will’t set upfront. For instance, we don’t wish to commit the Django SECRET_KEY worth in our repository, and we will’t know the database host URL till after the database is deployed.

However fortuitously, there’s a option to make these parameter values be injected on runtime. The parameters which include delicate info, such because the Django secret key or database secrets and techniques are saved encrypted within the AWS Secrets and techniques Supervisor, whereas others just like the TLS certificates will be saved as textual content within the AWS SSM Parameter Retailer.

This stack teams these sort of parameters and permits us to cross them later to each the appliance stack and to the employees’ stack.


The app_secrets dictionary teams the surroundings variables required by the Django app which include delicate info. So as a substitute of hard-coding the values, we create ecs.Secret objects. This makes the worth to be injected into env vars in runtime when the containers begin.

The ecs.Secret can retrieve the worth from a secret saved within the secrets and techniques supervisor or from a parameter saved within the SSM Parameter Retailer. Additionally, the secrets and techniques in secrets and techniques supervisor will be saved (encrypted) as textual content or as JSON.

The Django secret key’s a string saved within the secrets and techniques supervisor, so we use secretsmanager.Secret.from_secret_name_v2 to construct the key object from the key title. Discover it is advisable create this secret manually within the AWS console, and the title ought to match the conference talked about earlier to work with this pattern code.

The database secret can also be saved within the secrets and techniques supervisor. However it’s mechanically created through the creation of the database, and its content material is a JSON map with the database host URL, database title, username, password, and so on. So, we use the key object created by the database stack, and we specify the JSON subject from which we wish to extract every worth.

The AWS API keys are saved within the secrets and techniques supervisor as textual content, and they’re retrieved the identical method because the Django secret key. (These AWS keys are utilized by django-storages to retailer static information in S3).

You will notice that CDK has mechanically created an empty stack class known as MyDjangoAppStack.


We are going to use this stack to create the infrastructure required to run our Django app, as proven under:

On this case, we wish to run our Django app as an ECS Service, with a fronting load balancer so we will scale horizontally. As it is a well-known structure sample, there’s a resolution assemble known as ApplicationLoadBalancedFargateService that we will use for this. We may also allow well being checks and auto-scaling.

First, as we wish our app to run over HTTPS in our personal area, then we want a TLS certificates. As certificates creation and verification can take a while, we received’t create it utilizing CDK, however we are going to import it.

You possibly can create the certificates from the AWS Console utilizing the AWS Certificate Manager (ACM). As soon as created, we saved the certificates ARN as a parameter in SSM Parameter Retailer and import it right here. Later, we instantiate a Certificates object utilizing acm.Certificate.from_certificate_arn. Then we are going to cross the certificates object within the certificates parameter, whereas setting the protocol to HTTPS and setting redirect_http=True to pressure HTTP requests to be redirected to HTTPS.

Lastly, setting public_load_balancer=True will make the load balancer to be positioned in our public subnets so it is uncovered to the web.

Then, we set the cluster the place the service will reside. We get it from the cluster parameter, and right here we are going to cross within the cluster that we created beforehand within the community stack. And we set task_subnets utilizing a ec2.SubnetSelection to make the Fargate duties to run in our personal remoted subnets; they’re accessible by the load balancer solely, and they aren’t uncovered to the web.

Subsequent, we set CPU and reminiscence limits, and a desired rely for Fargate duties. Now we have parametrized these settings within the stack so we will specify roughly capability and roughly redundant cases whereas instantiating the stack for various environments (staging, manufacturing) later.

We additionally have to outline the container picture choices in task_image_options. We would like the picture to be constructed from the supply code, so we use ecs.ContainerImage.from_asset and set the relative path to the Dockerfile in our repo, and the goal to construct (prod, on this case, to construct the manufacturing picture).

We specify the port on which the app is listening so the load balancer forwards requests to it. Lastly, we cross within the surroundings variables and secrets and techniques that we outlined beforehand.

For well being checks, we now have added a standing endpoint in our app at /standing/ which returns standing 200 Okay on GETs, so we set that URL within the configuration. We additionally set thresholds so an occasion (container) shall be marked as wholesome after three profitable well being checks, and will probably be marked as unhealthy after two failed well being checks.

This manner unhealthy cases shall be mechanically detected and changed.

For auto-scaling, Amazon helps completely different scaling insurance policies: Target Tracking Scaling, Step Scaling, and Scheduled scaling. On this case, as we all know the CPU utilization is correlated to the variety of requests being processed by our app, then we are going to use a goal monitoring coverage primarily based on CPU utilization metrics.

When the typical CPU utilization rises above 75%, a scale-out alarm will set off Service Auto Scaling so as to add one other job (growing the specified duties rely) to assist out with the elevated load. Conversely, when the typical CPU utilization drops under 75% for a sustained time period, a scale-in alarm will set off a lower within the service’s desired rely to unencumber sources and scale back prices.

We additionally set a minimal and a most job rely, and once more these settings are parameters so we will modify them for the completely different environments later. For instance, we might need a minimal of two duties in manufacturing to ensure excessive availability, however one job could also be sufficient for a testing/staging surroundings.

Lastly, we avoid wasting parameters associated to the duties within the SSM, which shall be helpful to run one-off duties later (for instance, to run Django instructions in ECS).


Registering a website and creating hosted zones require some validation steps and might take time so we received’t automate that with CDK. However we will do the primary steps within the AWS console after which import what we want.

So we are going to search for the hosted zone by area title. Then we are going to set some DNS information to make our domains level to our load balancer. As we’re utilizing CDK, if the load balancer is changed and the DNS modifications, then the DNS report shall be up to date too.

The queues

We are going to use Amazon Simple Queue Service (SQS) to create our queues. SQS is a managed service so we don’t have to provision or handle any servers, sources, or capability.

It’s extremely out there and helps storing an infinite variety of messages. This makes it ideally suited for use as a dealer with Celery. Neglect about having to observe if the queues are up or if storage is getting full with the prospect of dropping messages.


We use the sqs.Queue assemble to create a queue. We are going to use SQS because the dealer for Celery, and this would be the default queue. We save the URL of the queue in SSM so we will reference it later in celery settings.

The employees

The employees will learn messages from the SQS queue and can execute celery duties. We add one other stack for the employees to allow them to scale independently.


As soon as once more, it is a well-known sample so we will use an existent resolution assemble: QueueProcessingFargateService. A lot of the parameters are similar to those within the ApplicationLoadBalancedFargateService, so I’ll clarify those that are completely different.

For auto-scaling, this time we use a step scaling coverage primarily based on the variety of messages within the queue. The steps are specified as a dictionary in scaling_steps. For every step, we outline a decrease or higher threshold for the variety of messages within the queue metric, and an motion to take expressed as an increment or decrement within the variety of staff.

We permit this coverage to be modified from a parameter through the stack instantiation, however we additionally set some cheap defaults.

The docker picture is similar used within the Django app service, however we overwrite the command executed within the container to run the script that begins the Celery employee, as a substitute of beginning gunicorn.

The deployment stage

Now that we now have outlined stacks for all of the system parts, we are going to group them in a Stage.


This stage represents an occasion of the entire system, and it will make it simpler to create a number of environments for testing/staging or manufacturing later. So, right here we instantiate all of the stacks that we created beforehand. We additionally

Including CI/CD with CDK pipelines

CDK pipelines permit not solely deploying new variations of your app when your software code modifications, however additionally they can replace your infrastructure (and the pipeline itself!) when your infrastructure code modifications.

You possibly can mannequin your pipeline(s) because it most accurately fits your wants and in accordance with the branching mannequin that you’re utilizing. For instance, if you’re following the GitHub Flow, chances are you’ll wish to have a single pipeline triggered when PRs are merged into the grasp department.

This pipeline deploys to a staging surroundings the place you may run extra handbook or automated exams (end-to-end, integration, UI exams, and so on.), and deploys to the manufacturing surroundings after automated or handbook approval.

As a substitute, if you’re following GitFlow, chances are you’ll wish to have two pipelines: one triggered by commits within the growth department that can deploy to your staging surroundings, and the opposite one triggered by commits within the grasp department that can deploy to your manufacturing surroundings.

For this instance, we are going to select the GitHub Movement having two environments, staging and manufacturing, with a handbook approval step earlier than transferring to manufacturing. Then our pipeline could have the next steps:

  • Supply: This stage fetches the supply code out of your GitHub repo and triggers the pipeline each time you push new commits to it. The repository contains now each your software code and your infrastructure code. This works utilizing webhooks by an AWS CodeStar connection app that it is advisable create beforehand.
  • Construct: This stage performs a cdk synth. The output of that step is a cloud assembly, which is used to carry out all actions in the remainder of the pipeline.
  • UpdatePipeline: This stage makes the pipeline replace itself as wanted. For instance, in case you replace your infrastructure code so as to add a brand new deployment stage to the pipeline, the pipeline is mechanically up to date to replicate the modifications you made. This additionally implies that we have to deploy the pipeline manually solely the primary time, and after that, we will simply modify the pipeline code and push modifications to the repository.
  • PublishAssets: This stage builds and publishes the Docker photographs for the app, staff, and so on.. to Amazon Elastic Container Registry (Amazon ECR), to allow them to be used through the subsequent deployments.
  • Staging: The staging surroundings is created or up to date as wanted Right here the ECS companies are created or up to date with the final docker photographs. We use this surroundings to do some extra QA (e.g., integration exams, end-to-end exams, UI exams).
  • Manufacturing: After handbook approval, the picture is deployed to the manufacturing stage. The brand new picture is deployed updating the app in ECS. Rolling updates can be utilized to keep away from downtimes.

Let’s outline our pipeline then:


The stack pipeline is like some other stack, nevertheless it’ll include the pipeline plus the levels of our system for the completely different environments wherein we wish to deploy.

We create the pipeline utilizing the CodePipeline construct. Then we add a ShellStep the place we specify the supply repository and the instructions wanted to put in CDK, set up dependencies, and synthesize the infrastructure code.

We additionally present dockerhub credentials to make use of a regular dockerhub account with a better restrict on picture pulls. You could use a free account, however chances are you’ll hit the pull restrict in case you deploy a number of occasions a day. The ShellStep will generate the primary 4 levels of our pipeline: supply, construct, updatepipeline, and publishassets.

Then, we use our Stage class MyDjangoAppPipelineStage to instantiate two levels: one for staging known as MyDjangoAppStaging, and one for manufacturing known as MyDjangoAppProduction. Every one could have completely different settings. For instance, for staging, we use a database with minimal capability, allow the database auto-pause function, and restrict the variety of duties in ECS to scale back prices. However in manufacturing, we hold the database at all times working, and we set larger scaling limits.

We use the add_stage technique so as to add every stage to the pipeline within the order we wish the app to be deployed. So the staging stage first after which the manufacturing stage. We additionally set the pre parameter whereas including the manufacturing stage to dam the pipeline till handbook approval is given.

Now that the pipeline stack is outlined we have to instantiate it within the entry level,


All we have to do now’s deploy the pipeline utilizing the CDK consumer:

$ cdk deploy MyDjangoAppPipeline

(You possibly can see the deploy command in motion in this video).

First, CDK with synthesized the CloudFormation Stacks, and if there’s a syntax error or different points you will notice it in stdout. Then CDK will ask for affirmation earlier than creating roles, insurance policies, and safety teams. Enter ‘y’ for sure, and the deployment course of will begin. You will notice the deployment progress in your shell and as soon as completed you will notice the pipeline within the CodePipeline panel at the AWS Console.

After the pipeline is deployed, will probably be triggered and all of the stacks shall be created. You possibly can monitor the stacks’ creation within the CloudFormation panel at the AWS Console.

That is the one time it is advisable run the deploy command. The subsequent time you commit any modifications within the infrastructure code, or the app code, the pipeline will replace the infrastructure and can replace the ECS companies as wanted.

When utilizing Django is frequent to run shell instructions or scripts for issues like making a superuser for the again workplace, working a customized knowledge migration, or simply working any customized command or script wanted for any purpose.

However because the app runs inside remoted stateless containers, we will’t ssh into one among them to get a shell and execute our instructions (it’s not a bug; it’s a function!). However we will begin a one-off Fargate job in ECS, in the identical cluster and subnets, to run the command inside a brand new container.

As this job is ephemeral, as soon as the command exits the duty stops and the container is destroyed. We will set off the duty by calling the RunTask API, both utilizing the AWS CLI or the AWS SDK. As I choose writing Python code to shell scripts, I’ll use the Python SDK (boto3), and I’ll write a Python script to run any instructions in ECS this fashion.

You can find this helper script at /scripts/

The script will take a command and a deployment surroundings title (the title that we gave to our staging or manufacturing stage), and it’ll begin a job to execute the command in that surroundings.

How? Bear in mind the parameters that we saved in SSM within the completely different Stacks? This script will learn these SSM parameters, and it’ll construct the configuration wanted to run the duty in the correct cluster and with the identical settings because the Django app.

The docker picture would be the similar picture used for the Django app, however the docker command shall be overwritten. This manner, we will run Django instructions and, as the duty has entry to the database, we will use the Django ORM or something we want.


First, we have to set some env vars with the AWS config wanted by the script to name the AWS API:


You possibly can set these env vars in your shell, or you may set them in a .env file after which load them into your shell. In case you are utilizing Linux, chances are you’ll use the helper script positioned at /scripts/

$ . ./scripts/

Then, you may execute the run_cmd script. You possibly can run it with the -h choice to see the utilization assist:

(.venv) $ python ./scripts/ -h
utilization: [-h] --env ENV_NAME [--env-var ENV_VARS] command
Run a command as a fargate job in ecs, utilizing the identical container and settings utilized by the Apppositional arguments:
optionally available arguments:
-h, --help present this assist message and exit
--env ENV_NAME The surroundings the place the command shall be run: MyDjangoAppStaging or MyDjangoAppProduction.
--env-var ENV_VARS Set further env vars as --env-var NAME1=VALUE1 --env-var NAME2=VALUE2

Now, for instance, let’s create a superuser for our again workplace in staging:

(.venv) $ python ./scripts/ "python createsuperuser --noinput --username admin" --env MyDjangoAppStaging --env-var DJANGO_SUPERUSER_PASSWORD=MyPassWord123
Constructing execution config for MyDjangoAppStaging
Config loaded:
AWS Response:

The ultimate configuration and the AWS API response are printed to stdout so you may catch any errors. In the event you see no error, that implies that the duty has been began. You possibly can monitor the duty standing within the ECS Panel at the AWS Console, and you may watch the logs at CloudWatch.

Thanks for studying!

More Posts