Deployment
Deployment has two scopes that should not be confused:
- Local app development — running an app repo (Signals or Aggregator) on your machine for development. Covered in the Installation guides.
- Cloud deployment — standing up the full stack on AWS EKS from the
bluedots-automationrepository. That is what this page covers.
For the architecture behind this flow, see Infrastructure & Deployment Architecture; for how images get built and pinned, see CI/CD & Build Pipeline.
Prerequisites
Section titled “Prerequisites”| Tool | Minimum | Purpose |
|---|---|---|
aws CLI | 2.x | AWS auth/STS, EKS kubeconfig |
tofu (OpenTofu) | ≥ 1.6 | provision cloud resources |
terragrunt | ≥ 0.90 | wraps tofu; per-module state |
kubectl | ≥ 1.24 | talk to EKS |
helm | ≥ 3.12 | install the charts |
bash, git | 4.x / 2.x | run install.sh, clone |
Also required: an AWS account that can create VPC/EKS/IAM/S3 (plus an S3 bucket for OpenTofu remote state), a GitHub PAT with read:packages (GHCR_PAT) to pull images from GHCR, and DNS control for the public hostnames.
yq and a Makefile are not used — opentofu/aws/<env>/install.sh is the single entrypoint for both infrastructure and Helm.
1. Provision infrastructure
Section titled “1. Provision infrastructure”# pick / create an environment (template/ is the reference)cp -R opentofu/aws/template opentofu/aws/<env>cd opentofu/aws/<env>
# authenticate and confirm identityaws sso login # or export static keysaws sts get-caller-identity # must succeed
# review environment inputs (region, EKS sizing, hosts, network, SMTP/SMS)$EDITOR global-values.yaml
# one-shot infra bootstrap:# create_tf_backend → backup_configs → create_tf_resources → apply_gp3_default_scbash install.shThis creates the remote-state bucket, runs terragrunt run --all apply (network → eks → iam → storage → random_passwords → output-file), writes the EKS kubeconfig, makes gp3 the default StorageClass, and generates the three per-chart values files into the env directory.
Validate:
kubectl config current-context # the new clusterkubectl get nodes # Readykubectl get sc # gp3 (default)ls common-services-values.yaml signals-values.yaml aggregator-values.yamlbash install.sh preflight # tooling + cluster + values files2. Set required values
Section titled “2. Set required values”Some values are not auto-generated. Edit global-values.yaml before the final terragrunt apply (hosts, network), and the generated signals-values.yaml / aggregator-values.yaml after they’re written but before deploy (Google Maps key, SMTP user/app-password, MSG91 keys, OpenAI key, admin emails).
The aggregator’s global.signalstack.actingOrgId is a post-Signals step — see step 4.
3. Deploy the stack
Section titled “3. Deploy the stack”Deploy in strict dependency order. Either run the whole stack at once, or step through it gating on health.
export GHCR_PAT=ghp_xxxxxxxxxxxx # read:packages
# optional static checks (install nothing)bash install.sh lintbash install.sh dry_run
# full ordered deploybash install.sh deploy_all_servicesdeploy_all_services chains: preflight → create_namespaces_and_secrets → deploy_monitoring → deploy_common_services → deploy_signals → deploy_aggregator → fix_acme_issuer_uri.
Step-by-step equivalent (inspect health between releases):
bash install.sh create_namespaces_and_secretsbash install.sh deploy_common_services # Kong + cert-manager + Postgres + Rediskubectl -n common-services get pods,pvc # confirm Ready + bound BEFORE continuingbash install.sh deploy_signalsbash install.sh deploy_aggregator # Keycloak init runs after Postgres — slowestEach deploy_* runs helm upgrade --install … --wait, so it blocks until that release’s pods are Ready. It does not check cross-namespace dependencies — confirm common-services Postgres/Redis are Ready yourself before deploying Signals/Aggregator.
4. Set actingOrgId (after Signals)
Section titled “4. Set actingOrgId (after Signals)”cd opentofu/aws/<env>ORG_ID=$(./get-signalstack-org-id.sh) # network_service org id from the seeded DB# set in aggregator-values.yaml:# global:# signalstack:# actingOrgId: "<ORG_ID>"bash install.sh deploy_aggregator # redeploy with the value setWithout it, aggregator login fails with SIGNALSTACK_ORG_NOT_REGISTERED.
5. Validate & point DNS
Section titled “5. Validate & point DNS”helm list -A # monitoring, common-services, signals, aggregator = deployedkubectl get ingress -A # hosts match global-values.yamlkubectl get clusterissuer letsencrypt-prodkubectl get certificate -A # READY=True once ACME completes
# the Kong proxy LoadBalancer hostname to point DNS at:kubectl -n common-services get svc common-services-kong-proxyPoint A/CNAME records for the signals API, signals UI and aggregator hosts at the Kong proxy LoadBalancer. Once DNS resolves and certs are READY=True, the public URLs are live.
6. Teardown
Section titled “6. Teardown”bash install.sh cleanup_all_services # helm releases + namespaces, reverse orderbash install.sh destroy_tf_resources # terragrunt run --all destroy (needs AWS creds)Deploying this documentation site
Section titled “Deploying this documentation site”This site is Astro + Starlight, deployed to GitHub Pages via GitHub Actions.
One-time GitHub setup
Section titled “One-time GitHub setup”- Push the
bluedots-docsproject to a repository. - In Settings → Pages, set Source = GitHub Actions.
- Confirm
astro.config.mjshas the rightsite(nobaseneeded for a custom domain):Asite: 'https://docs-signals-dpg.bluedotseconomy.org',public/CNAMEfile pins the custom domain across deploys. If instead deploying to a GitHub Pages project URL (<org>.github.io/<repo>), setsiteto that origin and addbase: '/<repo>'.
The workflow
Section titled “The workflow”The bundled workflow at .github/workflows/deploy.yml builds with the official withastro/action and publishes on every push to main. After the first successful run, the site is live at the configured URL.
Local preview
Section titled “Local preview”pnpm installpnpm dev # local dev server (under /)pnpm build # production build to dist/pnpm preview # preview the production build
