DEPLOYMENT
JaduSpine on AWS (ECS/Fargate + ALB + Cloudflare DNS) — Playbook¶
This repo’s playground expects:
- WebSocket path:
/connection/websocket - HTTP API path:
/api - JWT (HS256) for client connections using
JADU_SPINE_JWT_SECRET(seeAUTH_README.md)
Target outcome:
- A single JaduSpine realtime server (Centrifugo under the hood) on ECS/Fargate behind an ALB (HTTPS).
- DNS handled by Cloudflare.
- Admin enabled (password + secret).
- HTTP API public (protected by API key).
0) Inputs you must decide¶
- AWS region (must match your ACM certificate region)
- JaduSpine domain (example):
jaduspine.example.com - Allowed origin(s) for browsers (example):
https://app.example.com - Where to deploy:
- an existing VPC + subnets (recommended), or
- create a new VPC (not included in this repo yet)
This repo includes a Terraform root module at infra/terraform/ which assumes you already have a VPC + subnets.
Prerequisites (local machine)¶
- Terraform installed: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
- AWS CLI installed and configured (Terraform uses your local AWS credentials)
Basic AWS CLI setup:
# 1) Install AWS CLI (macOS via Homebrew)
brew install awscli
# 2) Configure credentials (creates/updates ~/.aws/config and ~/.aws/credentials)
aws configure
# Optional: if you use profiles
aws configure --profile <profile-name>
# 3) Verify you’re authenticated to the expected AWS account
aws sts get-caller-identity
If you’re using a named profile, run Terraform with:
export AWS_PROFILE=<profile-name>
1) Create secrets in AWS (Secrets Manager recommended)¶
Create 4 secret values (values are up to you). These names are the runtime env var names your backend/Centrifugo will use:
JADU_SPINE_JWT_SECRETCENTRIFUGO_API_KEYCENTRIFUGO_ADMIN_PASSWORDCENTRIFUGO_ADMIN_SECRET
Important:
- In AWS Secrets Manager, the secret resource name/path can be anything. What matters is the secret value stored inside it.
- Terraform only needs the secret ARNs and will inject those values into the right container env vars.
Naming suggestion for the AWS secret resource name (optional):
/jaduspine/<env>/JADU_SPINE_JWT_SECRET/jaduspine/<env>/CENTRIFUGO_API_KEY/jaduspine/<env>/CENTRIFUGO_ADMIN_PASSWORD/jaduspine/<env>/CENTRIFUGO_ADMIN_SECRET
Paste the secret ARNs into infra/terraform/terraform.tfvars:
secret_arn_jadu_spine_jwt_secret→ secret ARN that containsJADU_SPINE_JWT_SECRETsecret_arn_centrifugo_api_key→ secret ARN that contains the API key value (used asCENTRIFUGO_API_KEYin your backend and injected asCENTRIFUGO_HTTP_API_KEYinto Centrifugo)secret_arn_centrifugo_admin_password→ secret ARN that containsCENTRIFUGO_ADMIN_PASSWORDsecret_arn_centrifugo_admin_secret→ secret ARN that containsCENTRIFUGO_ADMIN_SECRET
For our current setup (plain-text secrets), these are the values to paste:
secret_arn_jadu_spine_jwt_secret = "arn:aws:secretsmanager:us-east-1:408921634255:secret:jaduspine/prod/jwt_secret-FBP1UO"
secret_arn_centrifugo_api_key = "arn:aws:secretsmanager:us-east-1:408921634255:secret:jaduspine/prod/centrifugo_api_key-AjaXt1"
secret_arn_centrifugo_admin_password = "arn:aws:secretsmanager:us-east-1:408921634255:secret:jaduspine/prod/centrifugo_admin_password-vrzc6p"
secret_arn_centrifugo_admin_secret = "arn:aws:secretsmanager:us-east-1:408921634255:secret:jaduspine/prod/centrifugo_admin_secret-mZa8GX"
2) Create an ACM certificate (TLS) and validate it in Cloudflare¶
In AWS Certificate Manager (ACM), request a public certificate for:
jaduspine.example.com
Choose DNS validation. ACM will give you one (or more) DNS records like:
- Name:
_xxxx.jaduspine.example.com - Type:
CNAME - Value:
_yyyy.acm-validations.aws
In Cloudflare:
- Add the CNAME record(s) exactly as ACM shows.
- Set them to DNS only (grey cloud) for validation.
Wait until ACM shows the cert as Issued.
3) Configure Terraform variables¶
Copy the example file:
infra/terraform/terraform.tfvars.example→infra/terraform/terraform.tfvars
Fill in:
- your VPC + subnet IDs
- your
certificate_arn - your secret ARNs
- your allowed origins
4) Deploy (Terraform)¶
From repo root:
cd infra/terraform
terraform init
terraform plan -out tfplan
terraform apply tfplan
Terraform will output (at minimum) your ALB DNS name.
5) Point Cloudflare DNS at the ALB¶
In Cloudflare DNS:
- Create
CNAMErecord: - Name:
jaduspine - Target:
<alb-dns-name-from-terraform-output>
For fewer moving parts during initial setup:
- set it to DNS only (grey cloud)
After things work end-to-end, you can decide whether to proxy it via Cloudflare (orange cloud). If you do, use Full (strict) TLS mode and keep the ALB HTTPS listener.
6) Configure your apps¶
Frontend¶
Set:
NEXT_PUBLIC_CENTRIFUGO_URL=wss://jaduspine.example.com/connection/websocket
Backend¶
Set:
CENTRIFUGO_API_URL=https://jaduspine.example.com/apiCENTRIFUGO_API_KEY=<same as secret>JADU_SPINE_JWT_SECRET=<same as secret>
7) Verify¶
- Open
https://jaduspine.example.comand confirm you reach the server. - Confirm WebSocket connects from your app using:
wss://jaduspine.example.com/connection/websocket- Confirm backend publishing works via HTTP API:
https://jaduspine.example.com/apiwithCENTRIFUGO_API_KEY.
Notes (v1 constraints)¶
- Keep
desired_count = 1. Without Redis, multi-task scaling will cause some publishes to miss clients connected to other tasks. - Admin is enabled via password + secret (see
infra/jaduspine.config.prod.jsonfor the intended shape).
GCP Cloud Run Deployment (Docker)¶
Target outcome: a single JaduSpine realtime server (Centrifugo) running on Google Cloud Run behind Cloud Run's managed HTTPS. DNS handled by Cloudflare.
Prerequisites (local machine)¶
- Docker installed and running
- gcloud CLI installed: https://cloud.google.com/sdk/docs/install
- Authenticated:
gcloud auth loginandgcloud auth configure-docker <region>-docker.pkg.dev - A GCP project with Cloud Run and Artifact Registry APIs enabled
1) Create Artifact Registry repository¶
In the GCP console → Artifact Registry → Create Repository:
- Name:
jaduspine(or any name) - Format: Docker
- Region: choose your preferred region (e.g.
us-central1)
Note the full registry hostname: <region>-docker.pkg.dev/<project-id>/jaduspine
2) Build and push the image¶
From the repo root:
IMAGE=<region>-docker.pkg.dev/<project-id>/jaduspine/centrifugo:latest
docker build -t $IMAGE infra/docker/
docker push $IMAGE
3) Create the Cloud Run service (GCP console)¶
Go to GCP Console → Cloud Run → Create Service.
Container:
- Container image URL: the image you pushed above
- Container port: 8000
- Leave command and arguments blank
- Memory: 512 MiB, CPU: 1
Variables & Secrets tab — add these env vars:
| Name | Value |
|---|---|
JADU_SPINE_JWT_SECRET |
your HS256 JWT signing secret |
CENTRIFUGO_API_KEY |
your Centrifugo HTTP API key |
CENTRIFUGO_ADMIN_PASSWORD |
your admin UI password |
CENTRIFUGO_ADMIN_SECRET |
your admin UI secret |
CENTRIFUGO_ALLOWED_ORIGINS |
comma-separated origins, e.g. https://*.scenarix.ai,https://*.studiojadu.com,https://*.pages.dev,http://localhost:3000 |
Requests (under "Container(s), Volumes, Networking, Security"):
- Request timeout: 3600 seconds
- Maximum concurrent requests: 80
Execution environment: Second generation
Revision scaling:
- Minimum instances: 1 (prevents cold starts dropping WebSocket connections)
- Maximum instances: 1 (keep at 1 — Centrifugo without Redis cannot share state across instances)
Authentication: Allow unauthenticated invocations (public)
Click Create.
4) Point Cloudflare DNS at Cloud Run¶
Cloud Run gives you a URL like https://jaduspine-xxxx-uc.a.run.app.
In Cloudflare DNS:
- Create CNAME record:
- Name: jaduspine (for jaduspine.yourdomain.com)
- Target: jaduspine-xxxx-uc.a.run.app (without https://)
- Proxy: DNS only (grey cloud) initially
After things work end-to-end, you can enable Cloudflare proxy (orange cloud). If you do, set TLS mode to Full (strict) — Cloud Run provides a valid Google-managed certificate, and Flexible mode will send unencrypted traffic between Cloudflare and Cloud Run which may be rejected.
Cloud Run already terminates TLS, so no ACM certificate needed.
5) Configure your apps¶
Frontend¶
NEXT_PUBLIC_CENTRIFUGO_URL=wss://jaduspine.yourdomain.com/connection/websocket
Backend¶
CENTRIFUGO_API_URL=https://jaduspine.yourdomain.com/api
CENTRIFUGO_API_KEY=<same value as env var above>
JADU_SPINE_JWT_SECRET=<same value as env var above>
6) Verify¶
- Open
https://jaduspine.yourdomain.com— should reach the Centrifugo UI - Confirm WebSocket connects from your app:
wss://jaduspine.yourdomain.com/connection/websocket - Confirm HTTP API publish works: POST to
https://jaduspine.yourdomain.com/apiwithAuthorization: apikey <CENTRIFUGO_API_KEY>
Notes¶
- Keep max instances at 1. Without Redis, multiple instances cannot share connected client state — publishes will miss clients on other instances.
- Logs are available in GCP Console → Cloud Run → your service → Logs tab.