Deployments¶
Core API¶
How It Deploys¶
Core API deploys automatically on every push to main:
- Quality checks workflow runs (lint, typecheck, tests)
- On success, Deploy to stage workflow triggers:
- Authenticates to AWS via OIDC (role:
gh-oidc-deploy-core-api) - Builds the Docker image
- Tags with both the commit SHA and
latest - Pushes to ECR (
258618559895.dkr.ecr.eu-central-1.amazonaws.com/core-api) - 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:
- Quality checks workflow runs (Pint, PHPStan, Pest, asset build)
- On success, Deploy to stage workflow triggers:
- Authenticates to AWS via OIDC (role:
gh-oidc-deploy-core) - Sends SSM command to the dash EC2 instance
- SSM runs:
git pull,composer install,npm ci && npm run build,artisan migrate/cache,apache reload - 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
stagebranch → 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¶
- Ensure changes are on
mainand CI is green - Create a GitHub Release at cookiehub-com/vault-api/releases/new
- Tag with a semver version (e.g.
v1.1.0) - 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-adminprofile withDevOpsAdminRoleassumption (for rollback)
Edge API¶
How It Deploys¶
Edge API has two deploy paths:
- Stage: Push to
stagebranch → 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.