Skip to content

Deployments

Core API

How It Deploys

Core API deploys automatically on every push to main:

  1. Quality checks workflow runs (lint, typecheck, tests)
  2. On success, Deploy to stage workflow triggers:
  3. Authenticates to AWS via OIDC (role: gh-oidc-deploy-core-api)
  4. Builds the Docker image
  5. Tags with both the commit SHA and latest
  6. Pushes to ECR (258618559895.dkr.ecr.eu-central-1.amazonaws.com/core-api)
  7. Forces a new ECS deployment on stage-euc1-core-ecs-cluster
flowchart LR
    A[Push to main] --> B[Quality checks]
    B -->|pass| C[Deploy to stage]
    C --> D[Build image]
    D --> E[Push to ECR]
    E --> F[ECS redeploy]

Infrastructure

Component Value
ECR repo core-api
ECS cluster stage-euc1-core-ecs-cluster
ECS service core-api
Task definition stage-euc1-core-core-api
Resources 512 CPU / 512 MB memory
Port 3000
Health check GET /health
URL https://core-api.stage.cookiehub.net
Logs CloudWatch /ecs/stage-euc1-core-core-api

Checking Deployment Status

# Watch the GitHub Actions run
gh run list --repo cookiehub-com/core-api --limit 5

# Check ECS service status
aws ecs describe-services \
  --cluster stage-euc1-core-ecs-cluster \
  --services core-api \
  --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' \
  --region eu-central-1

# Check which image is running
aws ecs describe-task-definition \
  --task-definition stage-euc1-core-core-api \
  --query 'taskDefinition.containerDefinitions[0].image' \
  --region eu-central-1

Viewing Logs

# Tail recent logs
aws logs tail /ecs/stage-euc1-core-core-api \
  --follow \
  --region eu-central-1

# Search for errors in the last hour
aws logs filter-log-events \
  --log-group-name /ecs/stage-euc1-core-core-api \
  --start-time $(date -v-1H +%s000) \
  --filter-pattern "error" \
  --region eu-central-1

Rollback

To roll back to a previous version:

# 1. Find the commit SHA of the version you want
gh run list --repo cookiehub-com/core-api --limit 10

# 2. Re-tag that image as latest
ROLLBACK_SHA="<commit-sha>"
REPO="258618559895.dkr.ecr.eu-central-1.amazonaws.com/core-api"

aws ecr get-login-password --region eu-central-1 | \
  docker login --username AWS --password-stdin 258618559895.dkr.ecr.eu-central-1.amazonaws.com

docker pull $REPO:$ROLLBACK_SHA
docker tag $REPO:$ROLLBACK_SHA $REPO:latest
docker push $REPO:latest

# 3. Force new deployment
aws ecs update-service \
  --cluster stage-euc1-core-ecs-cluster \
  --service core-api \
  --force-new-deployment \
  --region eu-central-1

Manual Deploy

If CI is broken and you need to deploy manually:

# Authenticate to ECR
aws ecr get-login-password --region eu-central-1 | \
  docker login --username AWS --password-stdin 258618559895.dkr.ecr.eu-central-1.amazonaws.com

# Build and push
docker build -t core-api:latest .
docker tag core-api:latest 258618559895.dkr.ecr.eu-central-1.amazonaws.com/core-api:latest
docker push 258618559895.dkr.ecr.eu-central-1.amazonaws.com/core-api:latest

# Trigger redeploy
aws ecs update-service \
  --cluster stage-euc1-core-ecs-cluster \
  --service core-api \
  --force-new-deployment \
  --region eu-central-1

Required Access

  • GitHub: Write access to cookiehub-com/core-api (to push to main)
  • AWS: IAM user in the stage account with ECS/ECR permissions (only needed for rollback or manual deploy)

Core (Laravel Dashboard)

How It Deploys

Core deploys to stage on push to stage branch via SSM:

  1. Quality checks workflow runs (Pint, PHPStan, Pest, asset build)
  2. On success, Deploy to stage workflow triggers:
  3. Authenticates to AWS via OIDC (role: gh-oidc-deploy-core)
  4. Sends SSM command to the dash EC2 instance
  5. SSM runs: git pull, composer install, npm ci && npm run build, artisan migrate/cache, apache reload
  6. Waits for completion, then health checks the site
flowchart LR
    A[Push to stage] --> B[Quality checks]
    B -->|pass| C[Deploy to stage]
    C --> D[SSM SendCommand]
    D --> E[git pull + build]
    E --> F[Health check]

Infrastructure

Component Value
Server stage-euc1-core-dash (t3.small, Ubuntu 24.04)
Deploy via AWS SSM SendCommand
URL https://dash.stage.cookiehub.net
Runtime PHP 8.3, Node 18, Apache
Code path /home/cookiehub/dash

Rollback

# SSM into the instance
aws ssm start-session --target <instance-id> --region eu-central-1

# Revert to previous commit
cd /home/cookiehub/dash
git log --oneline -5          # find the good commit
git checkout <commit-sha>
composer install --no-dev --optimize-autoloader
php artisan config:cache && php artisan route:cache
sudo systemctl reload apache2

Required Access

  • GitHub: Write access to cookiehub-com/core (to push to stage)
  • AWS: IAM user with SSM access in the stage account (only for rollback)
  • VPN: Required for direct SSH access as alternative

Vault API

How It Deploys

Vault API has two deploy paths:

  • Stage: Push to stage branch → Quality checks pass → Deploy to stage
  • Production: Create a GitHub Release → Deploy to production
flowchart LR
    A[Push to stage] --> B[Quality checks]
    B -->|pass| C[Deploy to stage]
    C --> D[Build image]
    D --> E[Push to ECR]
    E --> F[ECS redeploy]
flowchart LR
    A[Create Release] --> B[Deploy to prod]
    B --> C[Build image]
    C --> D[Push to ECR]
    D --> E[ECS redeploy]

Infrastructure

Stage Production
AWS account 258618559895 759286286879
ECR repo vault-api vault-api
ECS cluster stage-euc1-vault-ecs-cluster prod-euc1-core-ecs-cluster
ECS service vault-api vault-api
Task definition stage-euc1-vault-vault-api prod-euc1-core-vault-api
OIDC role gh-oidc-deploy-vault-api gh-oidc-deploy-vault-api
GitHub secret AWS_DEPLOY_ROLE_ARN AWS_DEPLOY_ROLE_ARN_PROD
URL Internal ALB (stage VPC) vault-api.internal.cookiehub.net
Port 3000 3000
Health check GET /health GET /health
SSM params /vault/stage/vault-api/* /vault/prod/vault-api/*
Logs CloudWatch /ecs/stage-euc1-vault-vault-api CloudWatch /ecs/prod-euc1-core-vault-api

Deploying to Production

  1. Ensure changes are on main and CI is green
  2. Create a GitHub Release at cookiehub-com/vault-api/releases/new
  3. Tag with a semver version (e.g. v1.1.0)
  4. The deploy workflow triggers automatically

Checking Deployment Status

# Watch the GitHub Actions run
gh run list --repo cookiehub-com/vault-api --limit 5

# Check stage
aws ecs describe-services \
  --cluster stage-euc1-vault-ecs-cluster \
  --services vault-api \
  --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' \
  --region eu-central-1

# Check prod
aws ecs describe-services \
  --cluster prod-euc1-core-ecs-cluster \
  --services vault-api \
  --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' \
  --region eu-central-1 \
  --profile prod

Rollback

Stage

ROLLBACK_SHA="<commit-sha>"
REPO="258618559895.dkr.ecr.eu-central-1.amazonaws.com/vault-api"

aws ecr get-login-password --region eu-central-1 | \
  docker login --username AWS --password-stdin 258618559895.dkr.ecr.eu-central-1.amazonaws.com

docker pull $REPO:$ROLLBACK_SHA
docker tag $REPO:$ROLLBACK_SHA $REPO:latest
docker push $REPO:latest

aws ecs update-service \
  --cluster stage-euc1-vault-ecs-cluster \
  --service vault-api \
  --force-new-deployment \
  --region eu-central-1

Production

ROLLBACK_TAG="<previous-release-tag>"  # e.g. v1.0.0
REPO="759286286879.dkr.ecr.eu-central-1.amazonaws.com/vault-api"

aws ecr get-login-password --region eu-central-1 --profile prod-admin | \
  docker login --username AWS --password-stdin 759286286879.dkr.ecr.eu-central-1.amazonaws.com

docker pull $REPO:$ROLLBACK_TAG
docker tag $REPO:$ROLLBACK_TAG $REPO:latest
docker push $REPO:latest

aws ecs update-service \
  --cluster prod-euc1-core-ecs-cluster \
  --service vault-api \
  --force-new-deployment \
  --region eu-central-1 \
  --profile prod-admin

Required Access

  • GitHub: Write access to cookiehub-com/vault-api (push to stage, create releases)
  • AWS stage: IAM user in stage account (258618559895) with ECS/ECR permissions
  • AWS prod: prod-admin profile with DevOpsAdminRole assumption (for rollback)

Edge API

How It Deploys

Edge API has two deploy paths:

  • Stage: Push to stage branch → Quality checks pass → Deploy to stage
  • Production: Create a GitHub Release → Deploy to production (not yet configured)
flowchart LR
    A[Push to stage] --> B[Quality checks]
    B -->|pass| C[Deploy to stage]
    C --> D[Build image]
    D --> E[Push to ECR]
    E --> F[ECS redeploy]

Infrastructure

Stage
AWS account 258618559895
ECR repo edge-api
ECS cluster stage-euc1-edge-ecs-cluster
ECS service edge-api
Task definition stage-euc1-edge-edge-api
OIDC role gh-oidc-deploy-edge-api
GitHub secret AWS_DEPLOY_ROLE_ARN
CloudFront E1Q7WZ429TOWKP (forwards geo headers to ALB)
URL https://edge-api.stage.cookiehub.net
Port 3000
Health check GET /health
Readiness check GET /ready (SQS + ClickHouse)
SSM params /edge/stage/edge-api/*
SQS queue stage-euc1-edge-consent (shared with Lambda)
Logs CloudWatch /ecs/stage-euc1-edge-edge-api

Endpoints

Method Path Description
GET /health Liveness check
GET /ready Readiness (SQS + ClickHouse)
GET /docs Interactive API documentation
v3 (new)
POST /v3/session/register Create edge_token + session_proof
POST /v3/events Ingest widget event → SQS
GET /v3/consent/state/:edge_token Consent state lookup from CH
POST /v3/geo/resolve Resolve region from geo headers
v1 (legacy compat)
GET /v1/region Geo resolution (cookiehub-regions header, returns {r,c,s})
POST /v1/log Consent log (raw payload → SQS envelope)

Checking Deployment Status

# Watch the GitHub Actions run
gh run list --repo cookiehub-com/edge-api --limit 5

# Check ECS service status
aws ecs describe-services \
  --cluster stage-euc1-edge-ecs-cluster \
  --services edge-api \
  --query 'services[0].{desired:desiredCount,running:runningCount,status:status}' \
  --region eu-central-1

# Verify geo headers are working
curl -s -X POST https://edge-api.stage.cookiehub.net/v3/geo/resolve \
  -H "Content-Type: application/json" \
  -d '{"regions":["EU","C3"]}'

Rollback

ROLLBACK_SHA="<commit-sha>"
REPO="258618559895.dkr.ecr.eu-central-1.amazonaws.com/edge-api"

aws ecr get-login-password --region eu-central-1 | \
  docker login --username AWS --password-stdin 258618559895.dkr.ecr.eu-central-1.amazonaws.com

docker pull $REPO:$ROLLBACK_SHA
docker tag $REPO:$ROLLBACK_SHA $REPO:latest
docker push $REPO:latest

aws ecs update-service \
  --cluster stage-euc1-edge-ecs-cluster \
  --service edge-api \
  --force-new-deployment \
  --region eu-central-1

Required Access

  • GitHub: Write access to cookiehub-com/edge-api (push to stage, create releases)
  • AWS stage: IAM user in stage account (258618559895) with ECS/ECR permissions

Atrax

Atrax has two deployable images, in two different regions:

Image ECR Cluster
atrax-api <acct>.dkr.ecr.eu-central-1.amazonaws.com/atrax-api {env}-euc1-core-ecs-cluster (shared with core-api)
atrax-node <acct>.dkr.ecr.eu-west-1.amazonaws.com/atrax-node {env}-euw1-atrax-ecs-cluster

There is no CI pipeline yet — both deploy via manual docker push + aws ecs update-service --force-new-deployment.

Manual deploy — atrax-api

export AWS_PROFILE={env}
ACCT=$(aws sts get-caller-identity --query Account --output text)
REPO="$ACCT.dkr.ecr.eu-central-1.amazonaws.com/atrax-api"

aws ecr get-login-password --region eu-central-1 | \
  docker login --username AWS --password-stdin "$ACCT.dkr.ecr.eu-central-1.amazonaws.com"

docker build -t atrax-api:latest .
docker tag atrax-api:latest "$REPO:latest"
docker push "$REPO:latest"

aws ecs update-service \
  --cluster {env}-euc1-core-ecs-cluster --service atrax-api \
  --force-new-deployment --region eu-central-1

Manual deploy — atrax-node

export AWS_PROFILE={env}
ACCT=$(aws sts get-caller-identity --query Account --output text)
REPO="$ACCT.dkr.ecr.eu-west-1.amazonaws.com/atrax-node"

aws ecr get-login-password --region eu-west-1 | \
  docker login --username AWS --password-stdin "$ACCT.dkr.ecr.eu-west-1.amazonaws.com"

docker build -t atrax-node:latest .
docker tag atrax-node:latest "$REPO:latest"
docker push "$REPO:latest"

aws ecs update-service \
  --cluster {env}-euw1-atrax-ecs-cluster --service atrax-node \
  --force-new-deployment --region eu-west-1

force-new-deployment is also how a running task picks up changed SSM values — the ECS agent only reads secrets at task launch.

Logs

# atrax-api
aws logs tail /ecs/{env}-euc1-atrax-atrax-api --follow --region eu-central-1
# atrax-node
aws logs tail /ecs/{env}-euw1-atrax-atrax-node --follow --region eu-west-1

Vault

In progress

Vault deployment documentation pending — requires input from infra lead on current pipeline.