A GitHub Action to deploy projects on different AWS services, specifically EC2 instances, using Docker Compose.
action-deployer is a composite GitHub action designed to deploy Dockerized applications to AWS EC2 instances. It builds the Docker Compose configuration (ports, networks, volumes) directly from Dockerfile comments and deploys it alongside the image produced by action-dockerization.
This action is designed to automate the deployment process, allowing you to manage your EC2 infrastructure efficiently by running Docker containers on your instances with the help of Docker Compose.
- This action must run after
actions/checkoutin the same job, since it reads the Dockerfile from the repository to derive the Docker Compose configuration. - It's typically used alongside action-dockerization, which builds the Docker image tar (
IMAGE_TAR_PATH) that this action copies and deploys. Pass the sameDOCKERFILE_PATHto both actions.
- Required:
true - Description: The delivery/deployment method.
EC2delivers and deploys the image to an EC2 instance via SCP/SSH.ECRpushes the image to an Amazon ECR repository (DELIVERphase only — see Delivering to ECR).
- Required:
true - Description: The IP address of the EC2 server to deploy the application to.
- Required:
true - Description: The user used to log into the EC2 instance.
- Required:
true - Description: The PEM key for SSH access to the EC2 instance.
- Required:
true - Description: Absolute path to the
.tarfile with the Docker image to be copied and deployed.
- Required:
false(required whenMETHODisECR) - Description: Name of the Amazon ECR repository to push the image to. Created automatically if it doesn't already exist. AWS credentials and region must already be configured in the job before this step (e.g. via
aws-actions/configure-aws-credentials).
- Required:
false - Default:
Dockerfile - Description: Relative path to the Dockerfile to parse for ports, networks and volumes. Should be the same value passed to
action-dockerization.
- Required:
false - Description: Base name for the generated Docker Compose file (
<COMPOSE_NAME>.yml). Defaults to the repository name if not provided.
-
Required:
false -
Description: Which phase of the deployment to run:
DELIVER,DEPLOY, or omitted to run both.DELIVER: copies the image to the EC2 instance, loads it into Docker, and prepares/updatesdocker-compose.ymlwith the parsed ports/networks/volumes. The currently running container is left untouched — no downtime.DEPLOY: swaps the running container for the image already loaded by a previousDELIVERrun (docker-compose up -d --force-recreatescoped to that service) and removes the old image. Doesn't copy anything or parse the Dockerfile.- Omitted: runs
DELIVERfollowed byDEPLOYin the same job, matching the previous all-in-one behavior.
Splitting the two phases lets you prepare a release (e.g. build + deliver in one workflow run) and trigger the actual swap later, independently — without having to pass image/Dockerfile information to the job that just flips the switch.
- Description: Full URI of the image pushed to ECR (
account.dkr.ecr.region.amazonaws.com/repository:tag). Only set whenMETHODisECRand theDELIVERphase ran; empty otherwise. See Delivering to ECR.
Ports, networks and volumes are derived automatically from comments in the Dockerfile pointed to by DOCKERFILE_PATH:
EXPOSE 3000
# TO 80
# NETWORK my-network
# VOLUME /host/path:/container/pathEXPOSE 3000+# TO 80→ maps host port80to container port3000.# NETWORK my-network→ attaches the service tomy-network.# VOLUME /host/path:/container/path→ bind-mounts/host/pathinto/container/path(only bind mounts are supported, not named volumes).
Any of these can be omitted; the container is deployed without ports, without extra networks, and/or without volumes accordingly.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Dockerize project
uses: matiascariboni/action-dockerization@v3
with:
IMAGE_ARCH: linux/amd64
DOCKERFILE_PATH: Dockerfile
- name: Deploy to EC2
uses: matiascariboni/action-deployer@v2
with:
METHOD: 'EC2'
EC2_IP: ${{ secrets.EC2_IP }}
EC2_USER: ${{ secrets.EC2_USER }}
EC2_KEY: ${{ secrets.EC2_KEY }}
IMAGE_TAR_PATH: ${{ github.workspace }}/${{ steps.dockerize.outputs.IMAGE_NAME }}.tar
DOCKERFILE_PATH: Dockerfile- Validation: The action validates all required inputs (and
ACTION, if provided) and displays configuration details for debugging. - Script Preparation: Makes deployment scripts executable.
- Compose configuration: Resolves the Docker Compose file name. If
ACTIONisDEPLOY, the Dockerfile isn't required to exist and isn't parsed, since that phase doesn't need ports/networks/volumes. - SSH Setup: Prepares the EC2 PEM key and establishes SSH connectivity to the target instance.
- Delivery (skipped if
ACTIONisDEPLOY): ifMETHODisEC2, copies the Docker image file to the EC2 instance via SCP, then runsec2_deliver.shremotely to install Docker/Docker Compose if needed, load the image, and prepare/updatedocker-compose.yml. IfMETHODisECR, runsecr_deliver.shlocally to load the image, create the ECR repository if needed, and push the image to it. Either way, the currently running container isn't touched. - Deploy (skipped if
ACTIONisDELIVER, and a no-op ifMETHODisECR): runsec2_deploy_run.shremotely to force-recreate the service's container with the already-loaded image and remove the old image, minimizing downtime.
The action provides detailed logging at each step:
- Input parameter validation and display
- Dockerfile parsing (ports, networks, volumes) and compose file name resolution
- SSH connection establishment
- File transfer progress
- Delivery phase (image loading, compose configuration, formatting)
- Deploy phase (container swap, old image removal, network cleanup)
All critical operations include error checking with clear failure messages to help troubleshoot deployment issues.
By default (no ACTION input) the action delivers and deploys in the same run, same as before. To split them across separate jobs or workflow runs:
# Job/run 1 — has the Dockerfile and the built image
- name: Deliver to EC2
uses: matiascariboni/action-deployer@v2
with:
METHOD: 'EC2'
ACTION: 'DELIVER'
EC2_IP: ${{ secrets.EC2_IP }}
EC2_USER: ${{ secrets.EC2_USER }}
EC2_KEY: ${{ secrets.EC2_KEY }}
IMAGE_TAR_PATH: ${{ github.workspace }}/${{ steps.dockerize.outputs.IMAGE_NAME }}.tar
DOCKERFILE_PATH: Dockerfile
# Job/run 2 — only needs EC2 credentials and IMAGE_TAR_PATH (its basename identifies the service), not the Dockerfile.
# The tar file itself doesn't need to exist on this runner — only the path's basename is used.
- name: Deploy on EC2
uses: matiascariboni/action-deployer@v2
with:
METHOD: 'EC2'
ACTION: 'DEPLOY'
EC2_IP: ${{ secrets.EC2_IP }}
EC2_USER: ${{ secrets.EC2_USER }}
EC2_KEY: ${{ secrets.EC2_KEY }}
IMAGE_TAR_PATH: ${{ github.workspace }}/${{ steps.dockerize.outputs.IMAGE_NAME }}.tarDEPLOY only needs IMAGE_TAR_PATH (its basename, without .tar) to know which service to swap inside the shared docker-compose.yml — it doesn't re-copy or re-load anything, so the Dockerfile/build context don't need to be available in that job.
METHOD: 'ECR' pushes the image built by action-dockerization to an Amazon ECR repository instead of an EC2 instance. Only the DELIVER phase applies to this method — ACTION: 'DEPLOY' is a no-op when METHOD is ECR.
AWS credentials and region must already be configured in the job before this action runs — this action doesn't accept AWS credentials as inputs, it relies on whatever is already configured (e.g. by aws-actions/configure-aws-credentials). The image is pushed with the same tag it already has after being loaded from IMAGE_TAR_PATH — there's no separate IMAGE_TAG input.
The action exposes the pushed image's URI as the IMAGE_URI output (e.g. 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest), so downstream steps/jobs can reference it (e.g. to deploy it on ECS/EKS/Lambda) without reconstructing the URI themselves.
jobs:
deliver:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Dockerize project
id: dockerize
uses: matiascariboni/action-dockerization@v3
with:
IMAGE_ARCH: linux/amd64
DOCKERFILE_PATH: Dockerfile
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Deliver to ECR
id: deliver
uses: matiascariboni/action-deployer@v2
with:
METHOD: 'ECR'
ACTION: 'DELIVER'
ECR_REPOSITORY: 'my-app'
IMAGE_TAR_PATH: ${{ github.workspace }}/${{ steps.dockerize.outputs.IMAGE_NAME }}.tar
- name: Use the pushed image URI
run: echo "Pushed ${{ steps.deliver.outputs.IMAGE_URI }}"The action intelligently handles optional parameters:
- If no
# TOport mappings are found, the container runs without port mappings - If no
# NETWORKcomments are found, Docker's default networking is used - If no
# VOLUMEcomments are found, no volumes are mounted - Network sections are only created in the Compose file when networks are actually used
If deployment fails, check the GitHub Actions logs for:
- Input validation errors: Ensure all required secrets are set
- Dockerfile not found: Verify
DOCKERFILE_PATHpoints to an existing file relative to the repo root - SSH connectivity issues: Verify EC2 security groups allow SSH from GitHub Actions IPs
- File transfer errors: Check EC2 instance disk space and permissions
- Docker errors: Review the deployment script output for Docker/Compose issues
This action is licensed under the MIT License.