Skip to main content

6. First repo and pipeline

What it is​

The first backend service, created from our template, running on your laptop, and deployed to the dev environment by the pipeline when you push a git tag. After this step, nobody deploys by hand again.

Why we do it​

  • A pipeline from day one means every change reaches dev the same way, and prod later.
  • Starting from the template gives you the Code organisation layout, tests, Dockerfile and deploy files without copying another product.

Before you start​

  • Steps 3, 4 and 5 done: the repo acme-api exists, you can sign in to the dev account, and dev.example.com resolves.
  • Docker Desktop, Node (the version in the template's .nvmrc), Terraform, AWS CLI.
  • The current template repo name, from the tech lead.

How it fits together​

The detail of each box is in Release pipeline.

Steps​

1. Create the service from the template​

gh repo create example-co/acme-api --private --template <template-org>/<api-template>
gh repo clone example-co/acme-api && cd acme-api

2. Rename the template's CI identity​

The template uses placeholder names. Replace every one with this service's names:

PlaceholderBecomesFound in
template-apiacme-apipackage.json, README.md
ECR repositoryacme-apibuildspec.yml
Container nameapiappspec.yml, deploy/*/taskdef.json
Secret prefix/acme/<env>/api/deploy/*/taskdef.json
Log group/ecs/acme-dev-apideploy/dev/taskdef.json
grep -rn "template" --exclude-dir=node_modules . # must find nothing

3. Run it on your laptop​

cp .env.example .env
docker compose up -d # Postgres and Redis
npm ci
npm run migrate
npm run dev
curl localhost:3000/health # {"status":"ok"}

4. Create the dev infrastructure​

In acme-infra, the dev environment uses our Terraform modules for: the ECR repository, ECS cluster and service, load balancer with a target group checking /health, the database, SSM placeholders for secrets, the GitHub connection and the pipeline.

cd acme-infra/envs/dev
aws sso login --profile acme-dev
terraform init
terraform plan -out plan.tfplan
terraform apply plan.tfplan

5. Finish the GitHub connection by hand​

Terraform creates the connection to GitHub as Pending. A person must finish it once: Developer Tools, Settings, Connections, select it, Update pending connection, and install the AWS app on the example-co organisation.

6. Fill the secrets​

Terraform created the parameters with placeholder values. Set the real ones:

aws ssm put-parameter --profile acme-dev --overwrite --type SecureString \
--name /acme/dev/api/JWT_SECRET --value "$(openssl rand -base64 48)"

Rules for secrets are in Secrets and config.

7. Deploy by tag​

git switch dev
git tag dev-v-0.1.0
git push origin dev-v-0.1.0

Watch it in CodePipeline. Build takes a few minutes; CodeDeploy then starts the new version next to the old and moves traffic when /health passes.

Check it worked​

curl https://api.dev.example.com/health # {"status":"ok"}
aws ecs describe-services --profile acme-dev --cluster acme-dev \
--services api --query 'services[0].deployments[].rolloutState'

If it goes wrong​

You seeIt means
Pipeline fails at Source with an access errorThe GitHub connection is still Pending. Step 5
Build fails with "Bad substitution"buildspec.yml uses a bash-only feature; CodeBuild runs dash
Task fails with "unable to assume role"A <PLACEHOLDER> is left in taskdef.json. Step 2
Task stops with a secrets errorThe execution role cannot read /acme/dev/api/* or decrypt it
Health check fails but the app runsIt checks / and gets a redirect. Point it at /health
A merge did not deployMerges never deploy. Push a tag

Rules​

Rule

Every service has /health from the first commit, and the load balancer checks it.

Rule

Only the dev pipeline exists until dev has deployed cleanly. Then add staging and prod the same way.

Mistakes we made​

  • Two web apps were forked from a live one and kept its CI names. A dev tag on either would have deployed over the live app.
  • Merged code sat undeployed for hours while people suspected broken webhooks. The tag was missing.

Done when​

  • Service created from the template; no template names left.
  • Runs locally with /health returning 200.
  • Dev infrastructure applied; GitHub connection Available.
  • Secrets filled in SSM.
  • dev-v-0.1.0 deployed; https://api.dev.example.com/health returns 200.