Orkhan's Blog on software development

Using pre-built Docker images with Laravel Vapor to reduce deployment time

Vapor’s custom Docker runtimes are great for customizing environment dependencies, adding PHP extensions, changing configuration, etc.

But doing all of this requires building the deployment image from scratch, on every single deployment. If you are using tools like GitHub Actions to automate the deployment, increased deployment time will eat up your GitHub Actions minutes quickly.

A perfect example of this would be using the imagick PHP extension. Adding an imagick extension to the Dockerfile requires building it from a source, which can take a lot of time.

Consider the following production.Dockerfile:

FROM laravelphp/vapor:php82

RUN apk add --update --no-cache autoconf g++ imagemagick-dev \
    && pecl install imagick \
    && docker-php-ext-enable imagick 

COPY . /var/task

In this post, I will show how to use pre-built Docker image with Laravel Vapor and use it as base images for your application’s deployments.

We’ll cover the following steps:

  • Building the base Docker image
  • Pushing the base Docker image to a container registry. It could be Docker Hub, GitHub Container Registry, AWS ECR, etc. We’ll use AWS ECR in this example.
  • Using the base Docker image in your Laravel Vapor project

Building the base Docker image

Create a Dockerfile in the root of your project with similar content to the production.Dockerfile but without the COPY command:

FROM laravelphp/vapor:php82

RUN apk add --update --no-cache autoconf g++ imagemagick-dev \
    && pecl install imagick \
    && docker-php-ext-enable imagick 

The goal is to build a Docker image with all the dependencies, extensions, and configurations necessary without adding the project files.

Build this Docker image with a tag. I’m using vapor-base in this example:

docker build -t vapor-base .

Pushing the base Docker image to AWS ECR

Create a new repository in AWS ECR. You can choose either a public or private repository type.

Once you create the repository, the repository URI will look like this:

1234567890.dkr.ecr.AWS-REGION-1.amazonaws.com/vapor-base

Replace 1234567890 with your AWS account ID and AWS-REGION-1 with your AWS region.

Use AWS CLI, log in to your account, and retrieve the authentication token to authenticate your Docker client to the registry:

aws ecr get-login-password --region AWS-REGION-1 | docker login --username AWS --password-stdin 1234567890.dkr.ecr.AWS-REGION-1.amazonaws.com

Tag the previously built Docker image with the repository URI:

docker tag vapor-base:latest 1234567890.dkr.ecr.AWS-REGION-1.amazonaws.com/vapor-base:latest

Lastly, push the Docker image to the AWS ECR repository:

docker push 1234567890.dkr.ecr.AWS-REGION-1.amazonaws.com/vapor-base:latest

Using the base Docker image in your Laravel Vapor project

Now that we have the base Docker image in AWS ECR, we can use it in our Laravel Vapor project. Open production.Dockerfile and make the following changes:

FROM 1234567890.dkr.ecr.AWS-REGION-1.amazonaws.com/vapor-base:latest 

COPY . /var/task

As you can see here, with this kind of setup we are no longer building the base Docker image on every deployment. Instead, we are using our pre-built Docker image as a base image and just adding the project files on top of it for every deployment.

This will significantly reduce the deployment time.