# Deploying to production
Deploying an application to production can be a daunting task. In this section, we will cover a few different methods of deploying the containers to a production environment, from using docker-compose with a single host, to deploying the changes on an AWS Fargate cluster that can scale on demand.
I recommend reading Best Practices section before deploying to production, as we cover in-depth topics such as handling application state, databases and observability.
# Using docker-compose
WARNING
This solution only works when you only have a single server, and don't require to scale beyond that.
A full working Laravel 7 example can be found at https://github.com/danursu/laradocker-laravel-7.
If you've followed the local configuration guide, you should already have a
working production docker-compose.yml
file. The file should be self-explanatory, we are not using any volumes,
and we are injecting environment variables from a local file. This way our secrets are not committed to source control.
Notice
The deploy section of the configuration does not apply when running using docker-compose
.
Build and run the app, by executing the command on the production server:
docker-compose -f docker-compose.yml up -d --build
Your application is accessible on port 80 on your server.
# Reverse proxy + TLS
We recommend that you use a reverse proxy and enable HTTPS with Let's encrypt certificates, as described in advanced configuration.
You will need to add your website hostname as an environment variable on the nginx container, and remove port 80 from the exposed ports, as the traffic will be forwarded through the reverse proxy container.
nginx:
environment:
VIRTUAL_HOST: example.domain.com
Replace example.domain.com
with your actual domain name. This is the domain that Let's encrypt
will generate a certificate for.
Your application is accessible on port 443 on your server.
# Using docker swarm
Docker swarm requires you to use a registry, you can either use a public one like hub.docker.com or a private one like AWS ECR or Google Cloud GCR. You can also host your own registry, but this is outside of the scope of this article.
- First step is to enable swarm mode on your docker engine, if you haven't done so already.
docker swarm init
- The base
docker-compose.yml
file is configured to work withdocker swarm
out of the box.
# docker-compose.yml
version: "3.8"
services:
nginx:
build:
context: .
dockerfile: ./docker/nginx.Dockerfile
image: 999999999999.dkr.ecr.us-east-1.amazonaws.com/test:nginx
restart: always
depends_on:
- php
ports:
- "80"
networks:
- default
deploy:
replicas: 1
resources:
limits:
cpus: '0.50'
memory: 50M
reservations:
cpus: '0.25'
memory: 30M
restart_policy:
condition: any
delay: 5s
max_attempts: 5
window: 120s
php:
build:
context: .
dockerfile: ./docker/php.Dockerfile
image: 999999999999.dkr.ecr.us-east-1.amazonaws.com/test:php
working_dir: /app
env_file: .env
restart: always
expose:
- "9000"
deploy:
replicas: 1
resources:
limits:
cpus: '0.50'
memory: 128M
reservations:
cpus: '0.25'
memory: 64M
restart_policy:
condition: any
window: 120s
Note how we specify the container image, which is the path to the registry. In this particular example,
we point to AWS ECR. Replace 999999999999
with your AWS account id,
and us-east-1
with the region where your ECR registry is located.
Docker swarm allows us to specify more options about how each service should behave once deployed, and limit memory and CPU for each container. More about the options here https://docs.docker.com/compose/compose-file/#deploy
- We will use
docker-compose
command to push to our registry.
# Build the docker images
docker-compose -f docker-compose.yml build
# Push the images to the registry
docker-compose -f docker-compose.yml push
- Now SSH into your production server and copy the
docker-compose.yml
files
ssh user@your-server.example.com
mkdir -p /sites/laravel-app
// Copy the files accross
scp docker-compose.yml user@your-server.example.com:/sites/demo-app/
- Deploy the stack to
docker swarm
. (on your production server)
cd /sites/laravel-app/
docker stack deploy --compose-file docker-compose.yml -f docker-compose.prod.yml laravel-app
Note: laravel-app
is just a namespace in the swarm. You can name it anything you want, but each app requires one.
- Check that it's running
docker stack services laravel-app
Try connecting to your app http://your-server.example.com
# Removing the stack
To remove our test app type
docker stack rm laravel-app
# ECS Fargate
WARNING
At the moment of writing, the ECS integration with docker-compose is in beta. You will need to enable Experimental features from the docker preferences.
Before you begin, you will need to have AWS CLI configured with credentials that allow access to your AWS account. Follow the official AWS documentation to get the CLI configured.
TIP
The IAM user that is used to create the cluster must have AmazonECS_FullAccess policy. You can start with a user that has full access to test with, and then follow the least privilege access to restrict access to only necessary resources.
- Configure docker-compose to work with ECS, by creating a new context.
docker context create ecs aws
# You can name the context aws, or anything else you prefer
? Select AWS Profile new profile
? profile name default
? Region us-east-1
? Enter credentials
Successfully created ecs context "aws"
# Alternatively you can create the profile in non-interactive mode
docker context create ecs --profile default --region us-east-1 aws
If you receive the error this tool requires the "new ARN resource ID format".
then login to your AWS account
and go to https://console.aws.amazon.com/ecs/home?region=us-east-1#/settings.
Under Amazon ECS ARN and resource ID settings set all resources to Enabled.
- Create a new ECR repository. You can skip this step if you already have an ECR repository, or if you're planning to use a docker repository.
aws ecr create-repository --repository-name laravel
- Update the
docker-compose.yml
file to include your repository image. Find the image entry for nginx and php in thedocker-compose.yml
file and update it to point to the ECR repository you created in the previous step.
services:
nginx:
image: 999999999999.dkr.ecr.us-east-1.amazonaws.com/laravel:nginx
php:
image: 999999999999.dkr.ecr.us-east-1.amazonaws.com/laravel:php
Replace 999999999999
with your AWS account id, and us-east-1
with the region where your ECS service will be deployed.
- Build the application and start it up locally to ensure it works as expected
# Make sure you are using the local context
docker context use default
# Build and start up the application
docker-compose -f docker-compose.yml up --build
- Push the application images to docker registry or ECR. ECS will pull the images from the registry when deploying your application.
Optional, if you are using ECR you will need to authenticate from your terminal
aws ecr get-login-password --region us-east-1 | docker login -u AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com
Replace us-east-1
with the region where your registry is located, and <AWS_ACCOUNT_ID>
with your AWS account ID.
docker-compose -f docker-compose.yml push
- Deploy the application to ECS. Behind the scenes, docker will generate a cloudformation template that will be used to deploy the infrastructure and application.
# Switch context to aws
docker context use aws
# Deploy
docker compose up
- Check the application is running and logs
# This command will list running applications on ECS
docker compose ps
# This command will display the application logs
docker compose logs
- Check the application in the browser, run
docker compose ps
to fetch the public DNS that your application is running at, it will look something like:dockercomposelaravel8LoadBalance-2343241123.us-east-1.elb.amazonaws.com
.
Head over to http://dockercomposelaravel8LoadBalance-2343241123.us-east-1.elb.amazonaws.com
# Cleanup
To remove the ECS application and related infrastructure, run:
docker compose down
TIP
If docker compose down
command fails, you can login to your AWS account console, and navigate to Cloud Formation.
From there you can manually delete the stack.