npm.io
3.0.5 • Published 17h ago

@ossy/deployment-tools

Licence
ISC
Version
3.0.5
Deps
5
Size
68 kB
Vulns
0
Weekly
0

@ossy/deployment-tools

Infrastructure and CDK tooling for the Ossy platform EC2 host.

The host runs Caddy as a reverse proxy plus several Docker containers managed by systemd. Apps are deployed by running ossy app publish.

Architecture

Internet → Caddy (:443)
             ├── api.ossy.se          → ossy-api (:3001)
             ├── ossy.se              → ossy-website-ossy (:3002)  ← temporary
             ├── www.plexus-sanitas.com → ossy-website-plexus-sanitas (:3003)  ← temporary
             └── *.* (on-demand TLS)  → ossy-platform (:3000)
                                           └── loads build from CMS on first request

Caddy uses on-demand TLS: before issuing a certificate for any domain, it calls GET http://localhost:3001/api/v0/apps/ask?domain=<domain>. If the domain is registered (i.e. ossy app publish has been run for it), Caddy issues the cert and proxies to the platform runtime.

Services

All services run as Docker containers managed by systemd. Service files are written to the EC2 host by CDK user data at instance creation.

Service Unit file Port Image
Caddy caddy-route53.service 80, 443
Ossy API ossy-api.service 3001 ghcr.io/ossy-se/api:latest
Ossy Platform ossy-runtime.service 3000 ghcr.io/ossy-se/platform:latest
configured via services <name>.service per config per config

Additional services are generated automatically from the services array in platforms.json — see the Configuration section below.

Configuration — platforms.json

packages/infrastructure/platforms.json is the single source of truth for a platform. Each entry becomes a CDK Stage with its own set of stacks.

Field Required Description
platformName Yes Unique name, used as the CDK stage name and CloudFormation stack prefix
awsAccountId Yes AWS account ID
awsKeyPairName Yes EC2 key pair name for SSH access
awsInstanceClass No EC2 instance class (e.g. t3, t4g, m5). Defaults to t3
awsInstanceSize No EC2 instance size (e.g. small, medium, large). Defaults to small
sesDomains No Domains to configure SES email identity for
domains No Domains to create Route53 A records for, pointing to the EC2 Elastic IP. Supports wildcards (e.g. *.ossy.se).
services No Per-platform container services (see below)
dnsRecords No Additional DNS records by root domain (currently supports MX)
env No Environment variables written to /etc/environment on the EC2 host at boot. Never uploaded to S3.
services — container/domain mapping

Services come in two flavours: HTTP (routed through Caddy) and TCP/UDP (raw socket, bypasses Caddy). Both generate a systemd unit that pulls the image on every restart and forwards /etc/environment to the container.

HTTP service (default)

Generates a Caddy reverse-proxy block with Route53 TLS. The container must listen on port 3000.

"services": [
  {
    "name": "ossy-website-ossy",
    "domain": "ossy.se",
    "image": "ghcr.io/ossy-se/website-ossy:latest"
  }
]
Field Description
name Unique service name — Docker container name and systemd unit (<name>.service).
domain Hostname routed by Caddy to this container (Route53 TLS via dns route53).
image Docker image to pull and run.

Host ports are auto-assigned starting at 3002, counting only HTTP services (TCP entries are skipped). Appending new HTTP entries is safe; avoid reordering existing ones.

TCP/UDP service

No Caddy block. Ports are mapped directly (HOST:CONTAINER) and opened in the EC2 security group. Use this for game servers or any other raw socket protocol.

"services": [
  {
    "name": "minecraft",
    "type": "tcp",
    "image": "itzg/minecraft-server",
    "ports": [25565],
    "protocol": "tcp"
  }
]
Field Description
name Unique service name — Docker container name and systemd unit.
type Must be "tcp" to enable this mode.
image Docker image to pull and run.
ports Array of port numbers to expose on the host and open in the security group.
protocol "tcp" (default), "udp", or "both".

Fixed built-in services (always present, not configurable via services):

  • ossy-api — the Ossy API (ghcr.io/ossy-se/api:latest, port 3001)
  • ossy-runtime — the platform runtime serving all CMS-published apps (ghcr.io/ossy-se/platform:latest, port 3000, catch-all)

Custom domains: For customer-owned domains (e.g. woodhill.com), the customer adds an A record pointing to the Elastic IP at their registrar. No infrastructure change needed — Caddy's on-demand TLS issues the cert automatically once the domain is registered via ossy app publish.

Wildcard subdomains: Add *.ossy.se to domains for a Route53 wildcard A record. This covers any *.ossy.se subdomain without individual records.

MEDIA_REPOSITORY and MEDIA_CDN_DOMAIN_NAME are derived automatically from the provisioned S3 bucket and CloudFront distribution — do not set them in env.

To add a new platform, add an entry to platforms.json. No bucket name or CDN domain is needed; these are auto-generated by CloudFormation.

CDK Stacks

Each platform is a CDK Stage containing five stacks:

Stack Description
storage-static S3 bucket (auto-named), daily backup plan, CloudFront CDN for /media. Kept separate from the EC2 stack so storage resources are never touched during instance updates.
deployment-target EC2 instance with Elastic IP. Imports the S3 bucket from storage-static via cross-stack reference.
dns Route53 A records for all domains in platforms.json, pointing to the Elastic IP.
ses SES email sending identity and DKIM records.

The Elastic IP ensures the EC2 instance can be replaced (instance type changes, user data updates) without breaking DNS or CloudFormation cross-stack exports.

Bootstrapping a new platform

cd packages/deployment-tools
npx cdk deploy 'ossybot/**' --profile ossybot --require-approval never

CDK provisions the instance, writes all service files, writes /etc/environment from platforms.json, and starts all services automatically. No manual SSH steps are required.

Note: EC2 user data only runs on first boot. To apply infrastructure changes to an existing instance you must terminate and recreate it, or SSH in and apply changes manually.

Deploying changes

Target a single platform:

npx cdk deploy 'ossybot/**' --profile ossybot

Target all platforms:

npx cdk deploy '**' --profile ossybot

Preview changes without deploying:

npx cdk diff 'ossybot/**' --profile ossybot

List all stacks:

npx cdk list

Redeploying after a code change

All services pull the latest image from GHCR on every restart — no manual build step needed.

# API — push to main triggers CI → ghcr.io/ossy-se/api:latest
sudo systemctl restart ossy-api

# Platform runtime — merge to main in ossy monorepo → ghcr.io/ossy-se/platform:latest
sudo systemctl restart ossy-runtime

# website-ossy — push to main in website-ossy repo → ghcr.io/ossy-se/website-ossy:latest
sudo systemctl restart ossy-website-ossy

# website-plexus-sanitas — push to main → ghcr.io/ossy-se/website-plexus-sanitas:latest
sudo systemctl restart ossy-website-plexus-sanitas

Environment variables

Defined in the env block of platforms.json, written to /etc/environment at boot. The following variables are injected automatically by CDK and should not be set manually:

Variable Source
MEDIA_REPOSITORY S3 bucket name from storage-static stack
MEDIA_CDN_DOMAIN_NAME CloudFront domain from storage-static stack

All other env vars:

Variable Used by Description
DB_URL API Atlas MongoDB connection string
DB_NAME API MongoDB database name
TOKEN_SECRET API JWT signing secret
AWS_ACCESS_KEY_ID API AWS credentials for S3
AWS_SECRET_ACCESS_KEY API AWS credentials for S3
OSSY_API_KEY Platform, websites API JWT for CMS reads
OSSY_COOKIE_SECRET Websites Cookie signing secret
OPENAI_API_KEY Worker OpenAI API key
OPENAI_ORGANIZATION Worker OpenAI organisation ID

Useful commands

SSH into instance

ssh -i path/to/keys ubuntu@<elastic-ip>

View service logs

journalctl -u ossy-runtime.service -n 200 -f
journalctl -u ossy-api.service -n 200 -f
journalctl -u ossy-website-ossy.service -n 200 -f
journalctl -u caddy-route53.service -n 200 -f

View running containers

docker ps

View container logs

docker logs ossy-runtime
docker logs ossy-api
docker logs ossy-website-ossy

Reload Caddy config

sudo systemctl reload caddy-route53.service