Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

stacksjs/ts-cloud

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

999 Commits
999 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Social Card of this repo

npm version GitHub Actions Commitizen friendly

ts-cloud

Zero-dependency cloud infrastructure as TypeScript. A driver-based system — deploy production-ready infrastructure to AWS or Hetzner with the same config, no cloud SDK or CLI required.

Overview

ts-cloud is a modern infrastructure-as-code framework that lets you define cloud infrastructure using TypeScript configuration files. Your config is provider-agnostic; a pluggable cloud driver translates it into provider-native API calls. Unlike AWS CDK or Terraform, ts-cloud:

  • Driver-Based Architecture - One CloudDriver interface, swappable backends. AWS and Hetzner drivers ship in the box.
  • Zero Cloud Dependencies - No AWS SDK, no Hetzner SDK, no CLIs. Direct, signed HTTPS API calls only.
  • Type-Safe Configuration - Full TypeScript types for all resources
  • Production-Ready Presets - 13 battle-tested infrastructure templates
  • Bun-Powered - Lightning-fast builds and deployments
  • CloudFormation Native - The AWS driver generates clean, reviewable CloudFormation templates

Cloud Drivers

Every provision, deploy, and teardown flows through the same CloudDriver interface — the CLI and your config never talk to a provider directly:

cloud.config.ts
      │
      ▼
resolveCloudProvider(config)          createCloudDriver({ config })
  cloud.provider, or auto-detected ───────────────►┌──────────────────────────┐
  from hetzner credentials                         │        CloudDriver       │
                                                   │                          │
                                                   │  provisionCompute  /     │
                                                   │  getComputeOutputs /     │
                                                   │  uploadRelease     /     │
                                                   │  runRemoteDeploy   /     │
                                                   │  destroyCompute          │
                                                   └────────────┬─────────────┘
                                                                │
                     ┌──────────────────────┬───────────────────┴──────┐
                     ▼                      ▼                          ▼
              ┌────────────┐        ┌──────────────┐           ┌─────────────┐
              │ AwsDriver  │        │ HetznerDriver│           │LocalBoxDriver│
              └────────────┘        └──────────────┘           └─────────────┘
Driver Compute Deploys via Release staging Infrastructure model
aws EC2 (CloudFormation stacks or a lightweight single-box boot) SSM AWS-RunShellScript S3 deploy bucket Clean, reviewable CloudFormation templates
hetzner Hetzner Cloud servers, cloud firewalls, private networks, native load balancers SSH + cloud-init bootstrap /var/ts-cloud/staging on the box Direct Hetzner Cloud API calls
local-box The machine ts-cloud runs on Local shell On-box management dashboard (box mode)

New providers implement the same interface (packages/core/src/drivers/types.ts) and register with the factory — DNS stays provider-agnostic via the separate DnsProvider abstraction (Cloudflare, Porkbun, GoDaddy, Route53).

Choosing a provider

// cloud.config.ts
export default {
  project: { name: 'My App', slug: 'my-app', region: 'us-east-1' },
  cloud: {
    provider: 'hetzner', // 'aws' (default) | 'hetzner'
  },
  // ...
}

Resolution order: an explicit cloud.provider wins; otherwise a hetzner.apiToken in the config file selects the Hetzner driver; with neither, the AWS driver is used (backward compatible). The token itself is supplied at deploy time — HCLOUD_TOKEN / HETZNER_API_TOKEN in the environment, or hetzner.apiToken in the config.

Deploy to Hetzner

Set the provider, supply a token, deploy:

export HCLOUD_TOKEN="your-hetzner-cloud-api-token"
cloud deploy
// cloud.config.ts
export default {
  // ...
  cloud: { provider: 'hetzner' },
}

The Hetzner driver boots an Ubuntu 24.04 server behind a cloud firewall, registers your deploy SSH key, and provisions the stack over cloud-init (nginx, PHP-FPM, bun/node/deno runtimes, databases, caches — installed via pantry). Fleets (a private network, dedicated services box, N app servers, and a load balancer) are first-class too. Server state is pinned in storage/cloud/state/<stack>.json — commit it, and CI reuses the same box instead of provisioning a new one.

Optional config (all env-overridable):

Config Env Default
hetzner.apiToken HCLOUD_TOKEN / HETZNER_API_TOKEN — (required)
hetzner.location HCLOUD_LOCATION fsn1
hetzner.image HCLOUD_IMAGE ubuntu-24.04
hetzner.sshPrivateKeyPath HCLOUD_SSH_KEY ~/.ssh/id_ed25519
hetzner.sshUser HCLOUD_SSH_USER root

Deploy to AWS

AWS is the default driver — just standard credentials:

export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
cloud deploy

Full infrastructure (VPC, ALB, RDS, ElastiCache, CloudFront, …) is generated as CloudFormation; the lightweight Forge-style path boots a single tagged EC2 box and deploys over SSM with releases staged in S3.

Programmatic drivers

import { createCloudDriver } from 'ts-cloud'

const driver = createCloudDriver({ config }) // AwsDriver | HetznerDriver (LocalBoxDriver in box mode)
const outputs = await driver.getComputeOutputs({ config, environment: 'production' })
// outputs.appPublicIp, outputs.appInstanceId, outputs.deployStoragePath, ...

Features

🌩️ Multi-Cloud by Design

One config, two production drivers today:

  • AWS - CloudFormation-generated stacks or a lightweight single-EC2 boot, SSM-based deploys, S3 release staging
  • Hetzner - Cloud servers + firewalls + private networks + load balancers over the Hetzner Cloud API, SSH deploys, cloud-init bootstrap

See Cloud Drivers for the architecture and per-provider setup.

🚀 Configuration Presets

Skip the boilerplate with production-ready presets for common architectures:

  • Static Sites - S3 + CloudFront for SPAs and static websites
  • Node.js Servers - EC2 + ALB + RDS + Redis for traditional apps
  • Serverless Apps - ECS Fargate + ALB + DynamoDB for scalable services
  • Full-Stack Apps - Complete frontend + backend + database stack
  • API Backends - API Gateway + Lambda + DynamoDB for serverless APIs
  • WordPress - Optimized WordPress hosting with RDS + EFS + CloudFront
  • JAMstack - Modern static sites with Lambda@Edge for SSR
  • Microservices - Multi-service architecture with service discovery
  • Real-time Apps - WebSocket API + Lambda + DynamoDB Streams
  • Data Pipelines - Kinesis + Lambda + S3 + Athena + Glue for ETL
  • ML APIs - SageMaker + API Gateway for ML inference
  • Traditional Web Apps - Session-based apps with EFS + Redis + ALB

🛠️ Infrastructure Builders

Complete CloudFormation template builders for:

  • Network - VPC, subnets, NAT gateways, routing, security groups
  • Storage - S3 buckets with versioning, encryption, lifecycle rules, EFS
  • Compute - EC2 Auto Scaling, ECS Fargate, Lambda functions
  • Database - RDS (PostgreSQL/MySQL), DynamoDB with streams and GSIs
  • Cache - ElastiCache Redis/Memcached with replication
  • CDN - CloudFront distributions with custom domains and Lambda@Edge
  • API Gateway - HTTP, REST, and WebSocket APIs
  • Queue - SQS queues with dead letter queues
  • Messaging - SNS topics and subscriptions
  • Monitoring - CloudWatch dashboards, alarms, and log groups
  • Security - ACM certificates, WAF rules, security groups

🔒 Security Posture and Deployment Gates

Persistent security findings and policy gates prevent unsafe releases:

  • 35+ Secret Patterns - AWS keys, API tokens, private keys, database credentials
  • Container Supply Chain - Local Trivy scanning, CycloneDX SBOMs, vulnerability summaries, and SLSA provenance
  • Environment Policies - Block, warn, or record by severity and scanner health
  • Auditable Remediation - Assignment, comments, expiring waivers, recurrence, and decision history
  • Posture Center - Responsive dashboard for findings, scanner health, policy editing, review, and export
# Scan for secrets before deploying
cloud deploy:security-scan --source ./dist

# Deploy with automatic security scanning
cloud deploy  # Scans automatically before deployment

See the security posture center documentation for policy, scanner, waiver, and release-artifact behavior.

☁️ Direct Provider Integration

No SDK, no CLI - pure signed HTTPS API calls against the providers themselves:

  • AWS - Signature V4 calls: CloudFormation (CreateStack, UpdateStack, DeleteStack, DescribeStacks), S3 (PutObject, multipart upload, sync directory), CloudFront (cache invalidations with wait support). Credentials resolve from env vars, ~/.aws/credentials, or IAM roles
  • Hetzner - Bearer-token Cloud API client with pagination, action polling, firewalls, private networks, load balancers, and SSH key management. Token resolves from HCLOUD_TOKEN / HETZNER_API_TOKEN or hetzner.apiToken

Quick Start

Installation

bun add ts-cloud

Your First Deployment

Create a cloud.config.ts:

import { createStaticSitePreset } from 'ts-cloud/presets'

export default createStaticSitePreset({
  name: 'My Website',
  slug: 'my-website',
  domain: 'example.com',
})

Deploy:

bun run cloud deploy

That's it! You now have:

  • ✅ S3 bucket with static website hosting
  • ✅ CloudFront CDN with HTTPS
  • ✅ Route53 DNS configuration
  • ✅ ACM SSL certificate

The same config deploys to Hetzner by setting cloud.provider: 'hetzner' and exporting HCLOUD_TOKEN — see Cloud Drivers.

More Examples

Full-Stack Application

import { createFullStackAppPreset } from 'ts-cloud/presets'

export default createFullStackAppPreset({
  name: 'My App',
  slug: 'my-app',
  domain: 'app.example.com',
  apiSubdomain: 'api.example.com',
})

Includes:

  • Frontend: S3 + CloudFront
  • Backend: ECS Fargate with auto-scaling
  • Database: PostgreSQL RDS with Multi-AZ
  • Cache: Redis ElastiCache
  • Queue: SQS for background jobs

Serverless API

import { createApiBackendPreset } from 'ts-cloud/presets'

export default createApiBackendPreset({
  name: 'My API',
  slug: 'my-api',
  domain: 'api.example.com',
})

Includes:

  • API Gateway HTTP API
  • Lambda functions with auto-scaling
  • DynamoDB tables with on-demand billing
  • CloudWatch monitoring and alarms

Configuration

Extending Presets

You can extend any preset with custom configuration:

import { createNodeJsServerPreset, extendPreset } from 'ts-cloud/presets'

export default extendPreset(
  createNodeJsServerPreset({
    name: 'My App',
    slug: 'my-app',
  }),
  {
    infrastructure: {
      compute: {
        server: {
          instanceType: 't3.large', // Upgrade instance type
          autoScaling: {
            max: 20, // Increase max instances
          },
        },
      },
    },
  }
)

Composing Presets

Combine multiple presets:

import { composePresets, createStaticSitePreset, createApiBackendPreset } from 'ts-cloud/presets'

export default composePresets(
  createStaticSitePreset({ name: 'Frontend', slug: 'frontend', domain: 'example.com' }),
  createApiBackendPreset({ name: 'Backend', slug: 'backend' }),
  {
    // Custom overrides
    infrastructure: {
      monitoring: {
        alarms: [{ metric: 'Errors', threshold: 10 }],
      },
    },
  }
)

Advanced Usage

Custom CloudFormation

Generate templates programmatically:

import { CloudFormationBuilder } from 'ts-cloud/cloudformation'

const builder = new CloudFormationBuilder(config)
const template = builder.build()

console.log(JSON.stringify(template, null, 2))

Direct AWS API Calls

Use the AWS clients directly:

import { CloudFormationClient, S3Client, CloudFrontClient } from 'ts-cloud/aws'

// CloudFormation
const cfn = new CloudFormationClient('us-east-1')
await cfn.createStack({
  stackName: 'my-stack',
  templateBody: JSON.stringify(template),
})

// S3
const s3 = new S3Client('us-east-1')
await s3.putObject({
  bucket: 'my-bucket',
  key: 'file.txt',
  body: 'Hello World',
})

// CloudFront
const cloudfront = new CloudFrontClient()
await cloudfront.createInvalidation({
  distributionId: 'E1234567890',
  paths: ['/*'],
})

Direct Hetzner API Calls

The Hetzner driver's Cloud API client is exported too:

import { HetznerClient } from 'ts-cloud'

const hcloud = new HetznerClient({ apiToken: process.env.HCLOUD_TOKEN! })
const servers = await hcloud.listServers() // fully paginated
const { firewall } = await hcloud.createFirewall({
  name: 'web-fw',
  rules: [{ direction: 'in', protocol: 'tcp', port: '443', source_ips: ['0.0.0.0/0', '::/0'] }],
})

Migrating object storage between providers

ts-cloud speaks the S3 API for AWS S3, Backblaze B2 and Hetzner Object Storage, so you can move a service's data off AWS (or between any two S3-compatible buckets) with a single command. Bytes are copied (not strings), so binary payloads — images, archives, mail attachments — survive intact, and Content-Type is preserved when the source reports it. The copy is idempotent (objects already present at the destination with the same size are skipped) and can verify itself afterwards.

cloud migrate:storage \
  --from aws:stacks-production-email \
  --to hetzner:stacks-mail \
  --include mailboxes/,inbox/,incoming/,sent/,trash/,drafts/,junk/,archive/,flags/,uids/,sms/ \
  --exclude mail-server,deploy/,_deploy/,imap-server/ \
  --verify

This copies only the mail data prefixes, deliberately leaving the server binaries and deploy artifacts behind (they show up in the report's EXCLUDED list so you can confirm nothing was missed), then re-lists the destination and asserts the object count and sizes match.

Flags

  • --from <provider:bucket> / --to <provider:bucket> — provider is aws, hetzner or backblaze (e.g. aws:my-bucket).
  • --from-region / --to-region, --from-endpoint / --to-endpoint — optional; default to the provider's standard endpoint.
  • --from-prefix / --to-prefix — key prefix on each side. The source prefix is stripped and the dest prefix prepended, so you can remap (email/inbox/a.emlmail/inbox/a.eml).
  • --include <csv> / --exclude <csv> — only copy / skip keys under these comma-separated prefixes (exclude always wins).
  • --dry-run — print the plan (what would copy / what is excluded) without writing.
  • --force — re-copy even if the destination already has an object of the same size.
  • --delete-extraneous — delete destination keys not present in the source (default OFF).
  • --concurrency <n> — max concurrent copies (default 8).
  • --verify — after copying, re-list the destination and assert object count + sizes match the copied set.

Credentials per provider

Set the credentials for both sides — the migrator resolves each side independently using the object-storage env conventions:

Provider Access key env Secret key env Region env
aws AWS_ACCESS_KEY_ID (or S3_ACCESS_KEY_ID) AWS_SECRET_ACCESS_KEY (or S3_SECRET_ACCESS_KEY) AWS_REGION
hetzner HETZNER_S3_ACCESS_KEY (falls back to S3_ACCESS_KEY_ID/AWS_ACCESS_KEY_ID) HETZNER_S3_SECRET_KEY (falls back to S3_SECRET_ACCESS_KEY/AWS_SECRET_ACCESS_KEY) HETZNER_S3_REGION
backblaze B2_APPLICATION_KEY_ID (falls back to S3_ACCESS_KEY_ID/AWS_ACCESS_KEY_ID) B2_APPLICATION_KEY (falls back to S3_SECRET_ACCESS_KEY/AWS_SECRET_ACCESS_KEY) B2_REGION

When both sides use the same credentials (e.g. one Hetzner project), the generic S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY pair covers them.

Programmatic API

The same migration is available as a library function (usable from scripts or stacks buddy):

import { migrateObjectStorage } from 'ts-cloud'

const result = await migrateObjectStorage({
  from: { provider: 'aws', bucket: 'stacks-production-email' },
  to: { provider: 'hetzner', bucket: 'stacks-mail' },
  include: ['mailboxes/', 'inbox/', 'sent/'],
  exclude: ['mail-server', 'deploy/'],
  verify: true,
})
// result: { copied, skipped, excluded, bytesCopied, errors, excludedKeys, deleted, verification }

DNS Providers

ts-cloud supports multiple DNS providers for domain management and SSL certificate validation:

Cloudflare

  1. Log in to your Cloudflare Dashboard
  2. Go to My ProfileAPI Tokens (or visit https://dash.cloudflare.com/profile/api-tokens)
  3. Click Create Token
  4. Use the Edit zone DNS template, or create a custom token with:
  • Permissions: Zone → DNS → Edit
  • Zone Resources: Include → All zones (or specific zones)
  1. Copy the generated token
export CLOUDFLARE_API_TOKEN="your-api-token-here"

Porkbun

  1. Log in to your Porkbun Dashboard
  2. Enable API access for your domain(s)
  3. Generate an API key pair
export PORKBUN_API_KEY="your-api-key"
export PORKBUN_SECRET_KEY="your-secret-key"

GoDaddy

  1. Log in to GoDaddy Developer Portal
  2. Create a new API key
  3. Note both the key and secret
export GODADDY_API_KEY="your-api-key"
export GODADDY_API_SECRET="your-api-secret"
export GODADDY_ENVIRONMENT="production"  # or "ote" for testing

Route53

Uses AWS credentials from environment or ~/.aws/credentials:

export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"
export AWS_HOSTED_ZONE_ID="Z1234567890"  # Optional

CLI Usage

# List domains
cloud domain:list --provider cloudflare

# List DNS records
cloud dns:records example.com --provider cloudflare

# Add a DNS record
cloud dns:add example.com A 192.168.1.1 --name api --provider cloudflare

# Generate SSL certificate with DNS validation
cloud domain:ssl example.com --provider cloudflare

Development

# Install dependencies
bun install

# Run tests
bun test

# Build
bun run build

# Type check
bun run typecheck

Architecture

How It Works

  1. Configuration - Define infrastructure in TypeScript (provider-agnostic)
  2. Driver Resolution - resolveCloudProvider picks aws or hetzner from your config; createCloudDriver builds the matching driver (cached per project)
  3. Template / API Translation - The AWS driver generates CloudFormation templates; the Hetzner driver composes Cloud API calls + cloud-init bootstraps
  4. Deployment - Create/update stacks with change sets (AWS), or provision servers/firewalls/networks directly (Hetzner)
  5. Release Deploys - Releases upload to provider staging (S3 / on-box) and roll out over SSM or SSH with zero-downtime cutovers
  6. Monitoring - Track deployment progress with real-time events

No Dependencies

ts-cloud uses zero external dependencies for cloud operations:

  • AWS Signature V4 - Manual request signing for authentication
  • Direct HTTPS - Native fetch() for AWS and Hetzner Cloud API calls
  • Credentials - Parse ~/.aws/credentials without SDK; bearer tokens for Hetzner
  • CloudFormation - XML/JSON parsing for responses

This means:

  • ⚡ Faster startup and execution
  • 📦 Smaller bundle size
  • 🔒 Better security (no supply chain attacks)
  • 🎯 Full control over provider interactions

Contributing

Please see CONTRIBUTING for details.

Community

For help, discussion about best practices, or any other conversation that would benefit from being searchable:

Discussions on GitHub

For casual chit-chat with others using this package:

Join the Stacks Discord Server

Postcardware

"Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where Stacks is being used! We showcase them on our website too.

Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎

Sponsors

We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.

License

The MIT License (MIT). Please see LICENSE for more information.

Made with 💙

About

Zero-dependency, modern infrastructure-as-code framework.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Sponsor this project

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.