Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy

By Kokil Thapa | Last reviewed: September 2026

Production teams outgrow manual SSH deploys fast. CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy gives you a managed path from Git push to running code on EC2 without babysitting every release. If you already run Laravel on AWS, as covered in our Laravel on AWS EC2 with RDS guide, these three services slot cleanly into that stack. This walkthrough covers IAM, buildspec files, pipeline stages, and CodeDeploy hooks you can copy today.

What is CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy?

Think of the trio as three specialised layers. CodePipeline is the conductor. It watches your repository, runs stages in order, and passes artifacts between them. CodeBuild is the compile-and-test worker. It spins up containers from a buildspec file, runs Composer, npm, PHPUnit, and packages output. CodeDeploy is the release engine on your servers. It copies files, runs lifecycle hooks, and rolls back if health checks fail.

On real client projects I have maintained sister legal-tech sites on shared EC2 using GitLab CI and Deployer 7. AWS-native CI/CD fits the same mental model: build once, deploy the same artifact everywhere, reload PHP-FPM after the symlink swap. The difference is AWS owns the runners and orchestration. You pay per build minute instead of patching a Jenkins box.

AWS CI/CD Service StackGit SourceCodeCommitCodePipelineOrchestratorCodeBuildBuild TestCodeDeployEC2 FleetS3 ArtifactsZip bundlesSecrets ManagerEnv varsEC2 Auto Scaling GroupCodeDeploy agent + PHP-FPM
Architecture of CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy from Git source to EC2 fleet

Each service has a narrow job. That separation keeps pipelines readable and lets you swap the source provider without rewriting deploy logic. CodePipeline supports GitHub, GitLab, Bitbucket, and CodeCommit as sources. Most teams I work with connect an existing GitHub repo through a CodeStar connection.

How the three services compare to self-hosted CI

CriteriaCodePipeline + CodeBuild + CodeDeploySelf-hosted (Jenkins / GitLab Runner)
Runner maintenanceAWS-managed build fleetYou patch OS, Docker, and agents
Cost modelPer build minute + pipeline stageFixed EC2 cost even when idle
EC2 deploy integrationNative CodeDeploy agent and hooksCustom SSH or Deployer scripts
Secret storageSecrets Manager / SSM Parameter StoreCI variables, often duplicated
Best fitTeams already on AWS EC2/ASGMulti-cloud or strict on-prem needs

If you are weighing hosts first, read our AWS vs DigitalOcean vs Hetzner for Laravel hosting comparison. AWS CI/CD pays off when your compute and database already live in the same account and VPC.

How do you set up a CodePipeline for a Laravel application?

Start with IAM roles before you click through the console wizard. CodePipeline needs a service role that trusts codepipeline.amazonaws.com. CodeBuild needs its own role with CloudWatch Logs, S3 artifact read/write, and Secrets Manager access. CodeDeploy needs a role on the EC2 instance profile so the agent can pull revisions from S3.

A typical Laravel pipeline on PHP 8.3 or 8.5 with Laravel 12 or 13.x has four stages:

  1. Source — trigger on push to main or on tag.
  2. Build — CodeBuild runs tests, compiles front-end assets with Node.js 26 LTS and npm 12, outputs a zip.
  3. Deploy (staging) — CodeDeploy to a staging Auto Scaling group.
  4. Deploy (production) — manual approval gate, then CodeDeploy to production.

Provision baseline infrastructure with CloudFormation or Terraform first. Our CloudFormation on AWS guide covers stack patterns that pair well with pipeline-driven deploys. The pipeline should not create EC2 instances on every run. It should deploy new application revisions to an existing fleet.

Create the pipeline via AWS CLI

aws codepipeline create-pipeline --cli-input-json file://pipeline.json

A minimal pipeline.json skeleton references your CodeBuild project and CodeDeploy application:

{
  "pipeline": {
    "name": "laravel-production",
    "roleArn": "arn:aws:iam::123456789012:role/CodePipelineServiceRole",
    "artifactStore": {
      "type": "S3",
      "location": "my-company-pipeline-artifacts"
    },
    "stages": [
      {
        "name": "Source",
        "actions": [{
          "name": "GitHub",
          "actionTypeId": {
            "category": "Source",
            "owner": "AWS",
            "provider": "CodeStarSourceConnection",
            "version": "1"
          },
          "configuration": {
            "ConnectionArn": "arn:aws:codestar-connections:...",
            "FullRepositoryId": "org/laravel-app",
            "BranchName": "main"
          },
          "outputArtifacts": [{"name": "SourceOutput"}]
        }]
      },
      {
        "name": "Build",
        "actions": [{
          "name": "BuildAndTest",
          "actionTypeId": {
            "category": "Build",
            "owner": "AWS",
            "provider": "CodeBuild",
            "version": "1"
          },
          "configuration": {
            "ProjectName": "laravel-build"
          },
          "inputArtifacts": [{"name": "SourceOutput"}],
          "outputArtifacts": [{"name": "BuildOutput"}]
        }]
      },
      {
        "name": "Deploy",
        "actions": [{
          "name": "DeployProduction",
          "actionTypeId": {
            "category": "Deploy",
            "owner": "AWS",
            "provider": "CodeDeploy",
            "version": "1"
          },
          "configuration": {
            "ApplicationName": "laravel-app",
            "DeploymentGroupName": "production-asg"
          },
          "inputArtifacts": [{"name": "BuildOutput"}]
        }]
      }
    ]
  }
}

Store database credentials in Secrets Manager, not in the buildspec. See manage secrets with AWS Secrets Manager and our broader CI/CD secrets management best practices article for rotation patterns.

Laravel Pipeline Stages1. SourceGit push2. BuildCodeBuild3. StagingCodeDeploy4. ApprovalManual gate5. ProdLive ASGBuild Output: app.zipvendor/ + public/build/ + appspec.ymlStored in S3 between stages
Typical CodePipeline stage flow for a Laravel app with staging, manual approval, and production deploy

What should you put in a CodeBuild buildspec.yml file?

CodeBuild reads buildspec.yml from your repository root by default. Version 0.2 is the current format. Define install, pre_build, build, and post_build phases explicitly. Cache Composer and npm directories to cut build times. Our CI/CD caching for Composer and npm guide applies the same principles inside CodeBuild cache paths.

version: 0.2

env:
  secrets-manager:
    DB_PASSWORD: prod/laravel:password

phases:
  install:
    runtime-versions:
      php: 8.3
      nodejs: 26
    commands:
      - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
      - composer --version

  pre_build:
    commands:
      - cp .env.ci .env
      - composer install --no-dev --prefer-dist --no-interaction --optimize-autoloader
      - npm ci

  build:
    commands:
      - php artisan config:clear
      - npm run build
      - vendor/bin/phpunit --colors=never

  post_build:
    commands:
      - rm -rf node_modules tests .git
      - zip -r app.zip . -x "*.env*"

artifacts:
  files:
    - app.zip
  name: laravel-build

cache:
  paths:
    - vendor//*
    - node_modules//*

Pin your PHP runtime in the CodeBuild project if you need 8.5. The console image may lag the anchor release. For Laravel 13.x you need PHP 8.3 minimum. Laravel 12 runs on PHP 8.2 or higher. Match the runtime on EC2 and in CodeBuild or you will chase extension mismatch bugs.

Fail builds on low test coverage

Add a coverage gate when the project warrants it. PHPUnit can emit Clover XML and you fail the build if coverage drops. Details sit in our code coverage gates in CI post. CodeBuild exposes CODEBUILD_BUILD_SUCCEEDING so post_build scripts can exit non-zero and mark the stage failed.

Validate buildspec syntax with the JSON formatter tool when you embed complex environment blocks. A trailing comma in inline JSON breaks the whole pipeline at create time.

Official reference: the AWS CodeBuild build specification reference lists every supported key and phase hook.

How does CodeDeploy work with EC2 and in-place vs blue/green?

CodeDeploy installs a lightweight agent on each EC2 instance. The agent polls for new deployments tied to a deployment group. You register instances by tag or Auto Scaling group membership. Each deployment follows instructions in appspec.yml at the root of your artifact zip.

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/laravel
permissions:
  - object: /var/www/laravel
    pattern: "**"
    owner: www-data
    group: www-data
hooks:
  BeforeInstall:
    - location: scripts/stop.sh
      timeout: 120
  AfterInstall:
    - location: scripts/migrate.sh
      timeout: 300
  ApplicationStart:
    - location: scripts/start.sh
      timeout: 120

Hook scripts live beside appspec in your repo. Keep them idempotent. A common pattern I've used on production Laravel applications:

  • BeforeInstall — put app in maintenance mode, drain queues.
  • AfterInstall — run php artisan migrate --force, clear caches.
  • ApplicationStart — reload PHP-FPM, disable maintenance mode.

CodeDeploy supports two compute platforms: EC2/on-premises and Lambda. For web apps on ASG, EC2 is the usual choice. Deployment configs control rollout speed:

ConfigBehaviourWhen to use
OneAtATimeSingle instance update, waits for successSmall fleets, low traffic
HalfAtATime50% concurrent updatesMedium ASG, acceptable brief capacity drop
AllAtOnceEvery instance simultaneouslyStaging only — never production
Blue/Green via ASGNew target group, traffic shift, tear down oldZero-downtime production releases

Blue/green on AWS pairs CodeDeploy with a second target group and an Application Load Balancer listener rule swap. The concept matches what we describe in CI/CD blue/green deployment explained. In-place deploys are simpler but briefly mix old and new code on different nodes. That can surface cache and schema race conditions.

In-Place vs Blue/Green DeployIn-PlaceBlue/GreenSame ASG instances updatedNew ASG cloned behind ALBEC2 v1EC2 v2Blue TGGreen TGRollbackIn-place: redeploy last S3 revisionBlue/green: shift ALB back to previous TG
CodeDeploy in-place rolling update compared with blue/green target group swap on Application Load Balancer

Install the CodeDeploy agent on Ubuntu 22.04 or 24.04 during AMI bake or user-data bootstrap. Verify with sudo service codedeploy-agent status. A missing agent is the number-one reason deploy stages hang at "In Progress". Official docs: CodeDeploy agent installation guide.

What are common CI/CD on AWS mistakes and how do you fix them?

Most failed pipelines I troubleshoot are IAM or path problems, not AWS bugs. The build succeeds, the deploy stage red bar appears, and someone SSHs in to fix it manually. That defeats the purpose of automation.

IAM and artifact path errors

CodeBuild must write to the artifact bucket. CodeDeploy must read from it. The EC2 instance role needs s3:GetObject on that bucket prefix. CodePipeline passes artifact names between stages. A typo in outputArtifacts or inputArtifacts gives you an empty zip on the deploy box.

Stale PHP opcache after deploy

Laravel on EC2 often runs PHP-FPM with opcache enabled. Copying new files does not reload workers. Your ApplicationStart hook should run sudo systemctl reload php8.3-fpm. I have seen this during production deployments on sister sites sharing a Deployer workflow. The same fix applies to CodeDeploy hooks.

Building assets on the server

Do not run npm on production EC2 unless you enjoy 512 MB instances swapping to death. Build front-end assets in CodeBuild with Vite 8.x and ship public/build inside the artifact. This mirrors the pattern in our GitLab CI pipeline for Laravel guide.

Skipping smoke tests post-deploy

Add a Lambda or CodeBuild post-deploy action that curls /health or runs an HTTP check against the ALB. Failing fast triggers automatic rollback via CloudWatch alarm on 5xx rate. Small teams benefit from the checklist in CI/CD best practices for small teams.

Pipeline Failure Decision TreeDeploy stage failed?Agent missingInstall codedeploy-agentIAM denied S3Fix instance roleHook timeoutCheck migrate logsSite shows old code?Reload PHP-FPM in ApplicationStart hookClear Laravel config and route cache
Troubleshooting decision tree for CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy deploy failures

Automate repetitive fixes with Boto3 scripts triggered from EventBridge when a pipeline state changes. Our automate AWS with Boto3 tutorial shows how to list failed executions and post to Slack.

For ongoing ops after go-live, pair the pipeline with support and maintenance services or Linux system administration so someone watches alarms and monthly AWS bills. Several sites on my shared EC2 pipeline — including work visible in our Notary Kathmandu portfolio case and Adventure Third Pole Trek booking platform — rely on disciplined deploy hooks rather than heroic midnight SSH sessions.

If you prefer VPS plus GitLab over native AWS tooling, the deploy Laravel with GitLab CI to a VPS guide is the closest alternative. Compare pipeline setup approaches in CI/CD pipeline setup for Nepal teams before committing to one vendor stack.

Scan commits for leaked keys before they reach CodeBuild. Pair pipeline secrets with git-level scanning described in secrets scanning in git and CI with Gitleaks. The AWS CodePipeline user guide remains the authoritative map of action providers and stage limits.

Key Takeaways

  • Wire IAM roles for CodePipeline, CodeBuild, and the EC2 instance profile before creating the pipeline — half of deploy failures are permission errors.
  • Build and test in CodeBuild with a versioned buildspec.yml; ship a zip artifact containing vendor/, compiled assets, and appspec.yml — never run npm on production EC2.
  • Use CodeDeploy lifecycle hooks to migrate, cache-clear, and reload PHP-FPM; opcache will otherwise serve stale bytecode after a successful copy.
  • Choose blue/green with ALB target group swaps for zero-downtime production; reserve in-place OneAtATime for staging or tiny fleets.
  • Store secrets in Secrets Manager, add manual approval before production, and attach post-deploy HTTP health checks to enable automatic rollback.
  • Cache Composer and npm directories in CodeBuild to keep per-minute build costs predictable on busy monorepos.

People Also Ask

Does CodePipeline replace Jenkins or GitLab CI entirely?

Not always. CodePipeline excels when your runtime is already on AWS EC2, Lambda, or ECS. Teams on multi-cloud VPS setups often keep GitLab CI or GitHub Actions for build and test, then trigger CodeDeploy through a thin deploy job. The build artifact format stays the same.

How much does AWS CodeBuild cost for a typical Laravel pipeline?

CodeBuild charges per build minute by compute type. A general1.small Linux build with cached dependencies often finishes a Laravel test suite in three to eight minutes. Budget roughly USD 0.005–0.02 per build at 2026 on-demand rates, plus S3 storage for artifacts. Busy teams running twenty builds daily still spend less than a dedicated t3.small Jenkins runner at roughly Rs 3,500/month (~USD 26).

Can CodeDeploy deploy to Lambda or ECS instead of EC2?

Yes. CodeDeploy supports Lambda function versions with traffic shifting and ECS blue/green through codedeploy hooks on task sets. The appspec format differs from EC2. Laravel apps on Lambda typically use Laravel Vapor rather than raw CodeDeploy, but the same pipeline can fan out to multiple deploy actions per environment.

What is the fastest way to roll back a bad CodeDeploy release?

Re-run the pipeline against the last known-good Git tag, or use the CodeDeploy console to redeploy the previous revision already stored in S3. Blue/green setups roll back by shifting the ALB listener back to the old target group without rebuilding. Keep at least five revision history entries in the deployment group configuration.

Ship repeatable releases on AWS

You now have the full path for CI/CD on AWS with CodePipeline, CodeBuild and CodeDeploy: IAM roles, a Laravel buildspec, pipeline stages, appspec hooks, and a deploy strategy that matches your traffic profile. Start with a staging deployment group, prove rollback works, then add the manual approval gate before production. Need hands-on help wiring this to an existing Laravel fleet or migrating from GitLab CI on EC2? Contact us or explore enterprise application development services for a production-ready pipeline audit.

Frequently Asked Questions

CodePipeline orchestrates stages from Git to EC2. CodeBuild runs tests and packages artifacts. CodeDeploy rolls revisions to your fleet with lifecycle hooks and rollback.

CodeBuild bills per build minute. A cached general1.small Linux run often finishes in three to eight minutes, roughly USD 0.005–0.02 per build plus S3 artifact storage.

Use blue/green with ALB target group swaps for zero-downtime production. Reserve in-place OneAtATime or HalfAtATime for staging or small fleets where brief mixed versions are acceptable.

Create IAM roles for CodePipeline, CodeBuild, and the EC2 instance profile first. Connect your GitHub repo through a CodeStar connection, then define four stages: Source on push to main, Build in CodeBuild, Deploy to staging, and Deploy to production behind a manual approval gate. Provision EC2 and Auto Scaling groups with CloudFormation or Terraform before the pipeline — it deploys revisions to an existing fleet, not new instances each run. Create the pipeline with aws codepipeline create-pipeline --cli-input-json file://pipeline.json referencing your CodeBuild project and CodeDeploy application.

CodePipeline needs a service role trusting codepipeline.amazonaws.com. CodeBuild needs its own role with CloudWatch Logs, S3 artifact read and write, and Secrets Manager access. CodeDeploy requires a role on the EC2 instance profile so the agent can pull revisions from S3. On real projects I have seen roughly half of failed deploys trace back to missing S3 GetObject on the artifact bucket prefix or a typo in artifact names between stages. Wire all three roles before opening the console wizard, not after the first red stage.

Use buildspec version 0.2 with install, pre_build, build, and post_build phases. Pin PHP 8.3 or 8.5 and Node.js 26 LTS with npm 12. Run composer install --no-dev, npm ci, npm run build with Vite 8.x, and PHPUnit in build. Pull DB_PASSWORD from Secrets Manager, not plain env files. Cache vendor and node_modules paths to cut minutes. In post_build, strip node_modules, tests, and .git, then zip the app as app.zip for CodeDeploy. Match PHP extensions between CodeBuild and EC2 or you will chase mismatch bugs after every release.

A lightweight CodeDeploy agent on each EC2 instance polls for deployments tied to a deployment group registered by tag or Auto Scaling group membership. Your artifact zip includes appspec.yml at the root plus hook scripts. CodeDeploy copies files to /var/www/laravel, runs BeforeInstall, AfterInstall, and ApplicationStart hooks, and rolls back if health checks fail. A typical Laravel pattern: maintenance mode and queue drain before install, php artisan migrate --force after install, then reload PHP-FPM and disable maintenance on start. Install the agent during AMI bake or user-data on Ubuntu 22.04 or 24.04.

Not always. CodePipeline fits best when compute and database already live in the same AWS account and VPC. Teams on multi-cloud VPS setups often keep GitLab CI or GitHub Actions for build and test, then trigger CodeDeploy through a thin deploy job — the artifact zip format stays the same. I maintain sister legal-tech sites on shared EC2 with GitLab CI and Deployer 7, which follows the same build-once-deploy-everywhere model. AWS-native CI/CD trades runner patching for per-minute billing. If you prefer VPS plus GitLab, that path remains a valid alternative for Nepal teams weighing vendor lock-in against operational simplicity.

With CodePipeline plus CodeBuild plus CodeDeploy, AWS manages the build fleet and you pay per build minute plus pipeline stages. Self-hosted Jenkins or GitLab Runner means you patch OS, Docker, and agents on fixed EC2 cost even when idle. CodeDeploy gives native agent and lifecycle hooks on EC2, whereas self-hosted setups typically rely on SSH or Deployer scripts. Secrets Manager and SSM Parameter Store centralise credentials instead of duplicating CI variables. Self-hosted wins for multi-cloud or strict on-prem needs. AWS-native CI/CD pays off when your Laravel app, RDS, and ALB already sit in one account.

OneAtATime updates a single instance and waits for success — fine for small fleets or low-traffic staging. HalfAtATime updates fifty percent concurrently, acceptable when brief capacity drop is tolerable on a medium Auto Scaling group. AllAtOnce hits every instance simultaneously and belongs on staging only, never production. Blue/green via a second target group and ALB listener rule swap gives zero-downtime production releases by shifting traffic before tearing down the old fleet. In-place rolling is simpler but briefly mixes old and new code across nodes, which can expose cache and schema race conditions on Laravel apps under load.

Use appspec version 0.0 with os linux. Map your artifact source to /var/www/laravel and set permissions to www-data for the tree. Define hooks with timeouts: BeforeInstall runs scripts/stop.sh to enter maintenance mode and drain queues. AfterInstall runs scripts/migrate.sh for php artisan migrate --force and cache clearing. ApplicationStart runs scripts/start.sh to reload PHP-FPM and exit maintenance. Keep hook scripts idempotent and ship them beside appspec.yml inside the zip CodeBuild produces. A missing or wrong destination path is a common reason deploy stages fail even when the build stage succeeded.

Put database credentials and API keys in AWS Secrets Manager, referenced from buildspec env secrets-manager blocks — never committed .env files or hard-coded values in pipeline JSON. CodeBuild's IAM role needs Secrets Manager read access scoped to the secrets it uses. The article also points to SSM Parameter Store as an option alongside Secrets Manager. Pair pipeline secrets with git-level scanning using Gitleaks so leaked keys never reach CodeBuild. For rotation patterns, follow your broader CI/CD secrets management practices. Storing secrets in the buildspec itself or artifact zip defeats the purpose of managed secret storage.

Most failures I troubleshoot are IAM or path problems, not AWS bugs. CodeBuild must write to the artifact S3 bucket and CodeDeploy's EC2 role needs s3:GetObject on that prefix. A typo in outputArtifacts or inputArtifacts delivers an empty zip to deploy boxes. Stale PHP opcache after file copy requires sudo systemctl reload php8.3-fpm in ApplicationStart — copying alone does not refresh workers. Do not run npm on production EC2; build front-end assets in CodeBuild and ship public/build inside the artifact. Add post-deploy HTTP checks against /health or your ALB so 5xx spikes trigger rollback via CloudWatch alarms instead of manual SSH fixes.

Production EC2 instances, especially t3.small or smaller with 512 MB RAM, will swap heavily if you run npm there. CodeBuild compiles Vite 8.x assets with Node.js 26 LTS during the build stage and packages public/build inside app.zip. The deploy stage only copies files and runs hooks — no Node runtime required on the fleet. This mirrors the pattern from GitLab CI Laravel pipelines where assets are built once in CI and shipped as artefacts. Server-side builds also introduce version drift between nodes during rolling deploys. Keep production boxes lean: PHP-FPM, the CodeDeploy agent, and your deployed artifact only.

Start by verifying the CodeDeploy agent is installed and running: sudo service codedeploy-agent status on Ubuntu 22.04 or 24.04. A missing agent is the number-one reason stages stall. Next confirm the EC2 instance profile can read the artifact from S3 and that appspec.yml paths match the actual deployment directory. Check hook script exit codes in /opt/codedeploy-agent/deployment-root/ logs — a failed migrate or permission error keeps the deployment open. Validate the instance is registered in the correct deployment group by tag or Auto Scaling group. If builds pass but deploy fails, compare artifact names between CodePipeline stages before SSHing in to patch manually.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: