Deployment Guide

Deploy CloudAtlas on a Linux VPS with Docker Compose and Traefik in under 10 minutes.

Before proceeding, make sure you've completed the Prerequisites and IAM Setup.
1

Provision a VPS

Any cloud VPS works: AWS EC2 (t3.small+), DigitalOcean Droplet, Hetzner CX22, etc. Minimum: 1 vCPU, 1 GB RAM, Ubuntu 22.04 or Debian 12.

Open inbound ports: 22 (SSH), 80 (HTTP/ACME), 443 (HTTPS).

If deploying on EC2, attach an IAM instance profile with CloudAtlasRootPolicy so you don't need access keys in the .env file.
2

Install Docker

Install Docker on Ubuntu / Debian
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
docker --version && docker compose version
3

Point your domain at the VPS

Add an A record in your DNS provider:

TypeNameValueTTL
Acloudatlas.example.comYOUR_VPS_IP300
4

Create the Traefik Docker network

Shell
docker network create traefik-public 2>/dev/null || true
5

Clone and configure

Shell
git clone https://github.com/babathakur855/CloudAtlas.git
cd CloudAtlas
cp .env.example .env
# Edit .env with your values, then:
docker compose build
docker compose up -d

Edit .env with your values:

.env
# VPS domain (Traefik HTTPS)
VPS_DOMAIN=cloudatlas.example.com

# Base AWS credentials β€” leave blank if using an EC2 instance role
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...

# Management account role CloudAtlas will assume as its identity
ROOT_ROLE_ARN=arn:aws:iam::123456789012:role/CloudAtlasRootRole

# Primary region for Organizations and STS calls
AWS_PRIMARY_REGION=us-east-1

# Role name created in child accounts (CloudAtlas creates this via Setup Roles)
INVENTORY_ROLE_NAME=CloudAtlasInventoryRole

# Role currently used when scanning each child account
# Automatically updated to INVENTORY_ROLE_NAME after a successful Setup Roles run
CROSS_ACCOUNT_ROLE=OrganizationAccountAccessRole

# AWS Organizations default admin role β€” used only during setup
ORG_ROLE_NAME=OrganizationAccountAccessRole

# Parallel scan threads per account (keep 5–10 to avoid throttling)
SCAN_MAX_WORKERS=5

# SQLite database path (inside the container volume)
DB_PATH=/app/data/cloudatlas.db
Never commit your .env file to Git. Add it to .gitignore before pushing.
6

Complete IAM setup

Before starting a scan, the inventory role must exist in every child account. You have two options:

Option A β€” Automated (greenfield / admin access)

Open Settings and click Setup Cross-Account Roles. CloudAtlas will assume OrganizationAccountAccessRole in each account and create the inventory role automatically.

Option B β€” Manual (client-managed environments)

Follow the IAM Setup Guide to get the exact policy JSON and CloudFormation template to give to your client.

7

Start the services

Shell
docker compose up -d

Verify the deployment:

Health checks
# Backend
curl http://localhost:8025/health

# Frontend (via Traefik domain)
curl https://cloudatlas.example.com/health
8

Run your first scan

Navigate to Dashboard and click Start Scan. The scan bar shows real-time progress per account.

A typical organisation with 10 accounts and 5 regions completes in 3–5 minutes.

GitHub Actions CI/CD

The repository includes a CI/CD pipeline at .github/workflows/deploy.yml that runs type-checking, then deploys automatically on every push to main.

Required GitHub Secrets

Go to Repository β†’ Settings β†’ Secrets and variables β†’ Actions β†’ New repository secret and add each of the following. AWS secrets use the CLOUDATLAS_ prefix so they never overlap with credentials from any other project. The Anthropic key is intentionally unprefixed and can be shared.

Secret nameValueShared?
VPS_HOSTVPS IP or hostnameMaybe
VPS_USERSSH user (root, ubuntu, deploy…)Maybe
VPS_SSH_KEYSSH private key β€” Ed25519 recommendedNo
VPS_DOMAINcloudatlas.example.comNo
CLOUDATLAS_AWS_ACCESS_KEY_IDAccess key for cloudatlas-deployer IAM userNo
CLOUDATLAS_AWS_SECRET_ACCESS_KEYSecret key for cloudatlas-deployer IAM userNo
CLOUDATLAS_AWS_REGIONus-east-1 (or your primary region)No
CLOUDATLAS_ROOT_ROLE_ARNarn:aws:iam::ACCT:role/CloudAtlasRootRoleNo
CLOUDATLAS_CROSS_ACCOUNT_ROLEOrganizationAccountAccessRoleNo
CLOUDATLAS_INVENTORY_ROLE_NAMECloudAtlasInventoryRoleNo
CLOUDATLAS_ORG_ROLE_NAMEOrganizationAccountAccessRoleNo
CLOUDATLAS_SCAN_MAX_WORKERS5No
ANTHROPIC_API_KEYsk-ant-… (shared across projects, no CLOUDATLAS_ prefix)Yes

Dedicated deployer IAM user

Create an IAM user called cloudatlas-deployer in the management account. This user's only permission is to assume CloudAtlasRootRole. All real permissions live on the role, not the user.

cloudatlas-deployer inline policy
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::MGMT_ACCOUNT_ID:role/CloudAtlasRootRole"
  }]
}

Then add this user as a trusted principal in CloudAtlasRootRole's trust policy:

CloudAtlasRootRole trust policy (add this principal)
{
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::MGMT_ACCOUNT_ID:user/cloudatlas-deployer"
  },
  "Action": "sts:AssumeRole"
}

Pipeline jobs

CI
All pushes + PRs β€” Python syntax check, TypeScript type check
Deploy
Push to main only (after CI passes) β€” SSH to VPS, write .env from individual secrets, build containers, start services, health check with auto-rollback
Summary
Always β€” Posts CI + deploy results to GitHub step summary

Operations

View logs

Shell
docker compose logs -f backend
docker compose logs -f frontend

Update CloudAtlas

Shell
git pull origin main
docker compose build --no-cache
docker compose up -d

Backup the database

Shell
docker compose cp backend:/app/data/cloudatlas.db ./cloudatlas-backup.db

Deployment checklist

VPS provisioned with Docker installed
Domain A record pointing to VPS IP
traefik-public Docker network created
cloudatlas-deployer IAM user created with sts:AssumeRole only policy
.env file configured with AWS credentials and ROOT_ROLE_ARN
IAM roles created (automated or manual)
docker compose up -d running successfully
Health checks pass
GitHub Secrets added (CLOUDATLAS_ prefix for AWS, no prefix for Anthropic)
First push to main triggers CI/CD pipeline successfully
First scan completed