Skip to main content

Your First Pipeline

This walkthrough builds a complete DevRamps pipeline for a Node.js application deployed to Amazon ECS, with Terraform-managed infrastructure.

Pre-existing AWS Infrastructure

This example references Terraform outputs like ecs_cluster_name and task_definition_arn. Your Terraform configuration in the infrastructure/ directory must define the ECS cluster, service, task definition, and related resources, and expose them as Terraform outputs. DevRamps does not create this infrastructure for you — Terraform does, as part of the pipeline.

By the end, you'll have a pipeline that:

  • Builds a Docker image from your application source.
  • Runs Terraform to provision infrastructure.
  • Deploys the container to ECS with rolling updates.
  • Waits for a bake period after deployment.
  • Deploys to staging first, then promotes to production.

Project Structure

Assume your repository looks like this:

my-app/
├── .devramps/
│ └── my-app/
│ └── pipeline.yaml # Pipeline definition
├── infrastructure/
│ ├── main.tf # Terraform configuration
│ ├── variables.tf
│ └── outputs.tf
├── src/
│ └── ... # Application source code
├── Dockerfile
└── package.json

Step 1: Define Your Stages

Start with two stages: staging and production. Each stage targets a different AWS account.

version: "1.0.0"

pipeline:
cloud_provider: AWS
pipeline_updates_require_approval: ALWAYS

stage_defaults:
deployment_time_window: PACIFIC_WORKING_HOURS

stages:
- name: staging
deployment_time_window: NONE
account_id: "111111111111"
region: us-east-1
vars:
env: staging
service_name: my-app

- name: production
account_id: "222222222222"
region: us-east-1
vars:
env: production
service_name: my-app

A few things to note:

  • pipeline_updates_require_approval: ALWAYS means changes to your pipeline definition will require approval before taking effect.
  • stage_defaults sets PACIFIC_WORKING_HOURS as the default deployment window for all stages. Staging overrides this to NONE so deployments can happen anytime.
  • vars defines stage-specific variables that you can reference in your steps using ${{ vars.env }}.

Step 2: Define Your Artifacts

Your pipeline needs a Docker image. Define it in the artifacts section:

  artifacts:
App Image:
type: DEVRAMPS:DOCKER:BUILD
rebuild_when_changed:
- /src
- /Dockerfile
- /package.json
params:
dockerfile: /Dockerfile
  • rebuild_when_changed tells DevRamps to only rebuild the image when files in these paths change. If you push a change that only touches your Terraform files, the image won't be rebuilt.
  • The artifact name (App Image) is how you reference it from your steps.

Step 3: Define Your Steps

Now define the deployment steps that run in each stage:

  steps:
- name: Synthesize Infrastructure
id: infra
type: DEVRAMPS:TERRAFORM:SYNTHESIZE
params:
requires_approval: DESTRUCTIVE_CHANGES_ONLY
source: /infrastructure
variables:
environment: ${{ vars.env }}
region: ${{ stage.region }}
aws_account_id: ${{ stage.account_id }}
service_name: ${{ vars.service_name }}
image_url: ${{ stage.artifacts["App Image"].image_url }}

- name: Deploy to ECS
type: DEVRAMPS:ECS:DEPLOY
goes_after: ["Synthesize Infrastructure"]
params:
cluster_name: ${{ steps.infra.ecs_cluster_name }}
service_name: ${{ steps.infra.ecs_service_name }}
reference_task_definition: ${{ steps.infra.task_definition_arn }}
image_url: ${{ stage.artifacts["App Image"].image_url }}

- name: Bake Period
type: DEVRAMPS:APPROVAL:BAKE
params:
duration_minutes: 5

Key details:

  • The id: infra field gives the Terraform step a short alias for use in expressions. Instead of ${{ steps["Synthesize Infrastructure"].ecs_cluster_name }}, you can write ${{ steps.infra.ecs_cluster_name }}.
  • goes_after explicitly controls step ordering. By default, steps run sequentially in the order listed. Using goes_after is useful when you want to run steps in parallel — steps without goes_after that are listed after a goes_after step won't wait for it. Here, Deploy to ECS explicitly depends on Terraform finishing first because it needs the Terraform outputs.
  • requires_approval: DESTRUCTIVE_CHANGES_ONLY means Terraform changes that destroy resources will pause for your review, while non-destructive changes apply automatically.
  • The Bake Period step waits 5 minutes after deployment, giving you time to verify the deployment is healthy before promoting to the next stage.

Step 4: Add Secrets (Optional)

If your Terraform configuration needs secrets (e.g., an API key), add them in the DevRamps dashboard:

  1. Go to your organization settings.
  2. Navigate to Secrets.
  3. Add a secret with a name like DATABASE_PASSWORD.
  4. Optionally scope it to a specific stage.

Then reference it in your pipeline:

variables:
db_password: ${{ organization.secrets["DATABASE_PASSWORD"] }}
# or for a stage-scoped secret:
db_password: ${{ stage.secrets["DB_PASSWORD"] }}

Secrets are never exposed in logs or the UI -- they're injected at runtime just before the step executes.

The Complete Pipeline

Here's the full pipeline.yaml:

version: "1.0.0"

pipeline:
cloud_provider: AWS
pipeline_updates_require_approval: ALWAYS

stage_defaults:
deployment_time_window: PACIFIC_WORKING_HOURS

stages:
- name: staging
deployment_time_window: NONE
account_id: "111111111111"
region: us-east-1
vars:
env: staging
service_name: my-app

- name: production
account_id: "222222222222"
region: us-east-1
vars:
env: production
service_name: my-app

steps:
- name: Synthesize Infrastructure
id: infra
type: DEVRAMPS:TERRAFORM:SYNTHESIZE
params:
requires_approval: DESTRUCTIVE_CHANGES_ONLY
source: /infrastructure
variables:
environment: ${{ vars.env }}
region: ${{ stage.region }}
aws_account_id: ${{ stage.account_id }}
service_name: ${{ vars.service_name }}
image_url: ${{ stage.artifacts["App Image"].image_url }}

- name: Deploy to ECS
type: DEVRAMPS:ECS:DEPLOY
goes_after: ["Synthesize Infrastructure"]
params:
cluster_name: ${{ steps.infra.ecs_cluster_name }}
service_name: ${{ steps.infra.ecs_service_name }}
reference_task_definition: ${{ steps.infra.task_definition_arn }}
image_url: ${{ stage.artifacts["App Image"].image_url }}

- name: Bake Period
type: DEVRAMPS:APPROVAL:BAKE
params:
duration_minutes: 5

artifacts:
App Image:
type: DEVRAMPS:DOCKER:BUILD
rebuild_when_changed:
- /src
- /Dockerfile
- /package.json
params:
dockerfile: /Dockerfile

About Terraform State

DevRamps automatically manages your Terraform state. State files are stored in an S3 bucket in your CI/CD account, and DevRamps configures the backend for you — you don't need to add a backend block to your Terraform configuration.

About Terraform Outputs

All outputs defined in your Terraform configuration automatically become available as step outputs. For example, if your Terraform has output "ecs_cluster_name" { value = aws_ecs_cluster.main.name }, you can reference it as ${{ steps.infra.ecs_cluster_name }} in subsequent steps.

Deploying

  1. Bootstrap: Run npx @devramps/cli bootstrap --dry-run first to preview, then npx @devramps/cli bootstrap to create IAM roles in both AWS accounts.
  2. Push: Commit and push your pipeline definition. DevRamps creates the pipeline and starts the first deployment.
  3. Monitor: Watch the deployment in the DevRamps dashboard. You'll see:
    • Artifact builds (the Docker image being constructed).
    • Staging steps executing (Terraform plan → apply → ECS deploy → bake).
    • Automatic promotion to production after staging succeeds.

What Happens When Something Fails?

If a step fails during deployment:

  1. The deployment stops at the failed step. No further steps run in that stage.
  2. The next stage does not start.
  3. You can view the step logs in the dashboard to understand what went wrong.
  4. Use the AI Failure Analysis feature to get an automated root cause analysis and suggested fix.
  5. Once you've fixed the issue, push the fix and DevRamps starts a new deployment. Or retry the failed stage to re-run with the same code.

Next Steps