LAB: GitLab CI/CD Docker Deploy to AWS EC2

Published: (February 20, 2026 at 07:02 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

๐ŸŽฏ Lab Objective

Build a full CI/CD pipeline that:

  • Connects GitLab to Mac terminal
  • Builds Docker image
  • Pushes image to GitLab Registry
  • SSH into EC2
  • Deploys container
  • Handles realโ€‘world production errors

๐Ÿ—๏ธ Architecture

Mac Terminal
     โ†“
GitLab Repo
     โ†“
GitLab CI/CD Pipeline
     โ†“
Docker Image Build
     โ†“
Push to GitLab Container Registry
     โ†“
SSH to AWS EC2
     โ†“
Docker Pull & Run
     โ†“
Application Live on Port 80

๐Ÿ”น Steps

STEPโ€ฏ1 โ€” GitLab Pages Basic Pipeline

.gitlab-ci.yml

image: busybox

pages:
  stage: deploy
  script:
    - echo "The site will be deployed"
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Trigger pipeline:

git commit --allow-empty -m "Trigger pipeline"
git push origin master

STEPโ€ฏ2 โ€” Add Docker Build Stage

Create Dockerfile (project root):

FROM nginx:alpine
COPY public /usr/share/nginx/html
EXPOSE 80

Update .gitlab-ci.yml:

stages:
  - build
  - push
  - deploy

variables:
  IMAGE_NAME: registry.gitlab.com/$CI_PROJECT_PATH:latest

build_image:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $IMAGE_NAME .

STEPโ€ฏ5 โ€” Push to GitLab Container Registry

Add the push job:

push_image:
  stage: push
  image: docker:24
  services:
    - docker:24-dind
  script:
    - echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
    - docker push $IMAGE_NAME

๐Ÿ”ด Errors & Fixes

ERROR #1 โ€” Image Not Found

Error

An image does not exist locally with the tag

Fix
Ensure the build and push stages use the same $IMAGE_NAME.

STEPโ€ฏ6 โ€” Deploy to AWS EC2

Deploy job:

deploy_ec2:
  stage: deploy
  image: alpine
  before_script:
    - apk add --no-cache openssh
  script:
    - echo "$EC2_KEY" > key.pem
    - chmod 600 key.pem
    - ssh -o StrictHostKeyChecking=no -i key.pem ubuntu@$EC2_HOST "
        docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY &&
        docker pull $IMAGE_NAME &&
        docker stop web || true &&
        docker rm web || true &&
        docker run -d -p 80:80 --name web $IMAGE_NAME
      "

ERROR #2 โ€” SSH Hostname Not Resolved

Cause
CI/CD variables not set.

Fix
Add the following variables in Settings โ†’ CI/CD โ†’ Variables (not protected, visible):

KeyValue
EC2_HOSTEC2 public IP address
EC2_KEYFull contents of the .pem file

ERROR #3 โ€” Portโ€ฏ80 Already in Use

Pipeline error

failed to bind host port 80
address already in use

Investigation

ssh -i key.pem ubuntu@$EC2_HOST
sudo lsof -i :80

Result shows nginx listening on portโ€ฏ80.

Fix

sudo systemctl stop nginx
sudo systemctl disable nginx

Reโ€‘run the pipeline โ†’ SUCCESS.

๐Ÿ”น Final Result

The application is accessible at:

http://

๐Ÿง  Real DevOps Troubleshooting Lessons

IssueTool Used
SSH deniedssh-add
Pipeline variable missingGitLab CI/CD variables
Docker tag mismatchInspect $IMAGE_NAME
SSH hostname errorVerify variables
Port conflictlsof -i :80
Nginx conflictsystemctl stop

These are typical productionโ€‘level debugging steps.

๐ŸŽ“ Interview Explanation Version

If an interviewer asks, โ€œHow would you deploy using GitLab CI/CD to EC2?โ€ you can answer:

  1. Create a Dockerfile for the application.
  2. Configure a multiโ€‘stage .gitlab-ci.yml pipeline (build, push, deploy).
  3. Build the Docker image and push it to the GitLab Container Registry.
  4. Store the EC2 SSH private key and host IP as CI/CD variables.
  5. In the deploy stage, SSH into the EC2 instance, log in to the registry, pull the image, stop/remove any existing container, and run the new container on portโ€ฏ80.
  6. Handle common issues (missing variables, port conflicts, existing services) to ensure an idempotent, productionโ€‘ready deployment.
0 views
Back to Blog

Related posts

Read more ยป

DumbQuestion.ai - '๐‰๐ฎ๐ฌ๐ญ ๐๐ฎ๐ข๐ฅ๐ ๐ˆ๐ญ' ๐๐ž๐œ๐จ๐ฆ๐ž๐ฌ ๐Ž๐ฏ๐ž๐ซ๐ฅ๐ฒ ๐Ž๐ซ๐ ๐š๐ง๐ข๐ณ๐ž๐ ๐š๐ง๐ ๐๐ซ๐ž๐ฉ๐š๐ซ๐ž๐

Continued from Part 1โ€ฆ Introduction โ€œLet the flow guide meโ€ sounded like a fun way to start a side project, but it lasted only about ten minutes. Even side proj...