Skip to main content

Get started with Sprintsail

Sprintsail is an opinionated TypeScript IaC SDK. You describe your application as a set of high-level primitives — Function, WebApp, Database, Bucket, Secret, ... — and the SDK provisions them on whichever target you point it at.

Today there are two targets:

  • aws — your AWS account. Primitives map to Lambda, ECS Express, RDS, S3, Secrets Manager, SQS, EventBridge.
  • sprintsail-runtime — a managed Kubernetes runtime running exclusively open-source operators (Knative, CloudNativePG, MinIO, RabbitMQ, sealed-secrets, Contour, cert-manager). Same application code, no proprietary lock-in.

The headline workflow sail migrate --from aws --to sprintsail-runtime moves a deployed app between the two — infrastructure, data, secrets — with the same handler code.

Install

Sprintsail ships as an npm package + a CLI binary.

npm install --save-dev @sprintsail/sdk @sprintsail/cli

The CLI is exposed as sail. Add a script or run it via npx:

npx sail --help

Node 20+ is required.

Create a project

A Sprintsail project is two files plus your application code.

sail.config.ts — project name and default target.

import { defineConfig } from '@sprintsail/cli/config';

export default defineConfig({
project: 'orders',
defaultTarget: 'aws:us-east-1',
});

infra.ts — primitives, declared as TypeScript objects.

import { Project, Database, Secret, WebApp } from '@sprintsail/sdk';

export const app = new Project('orders');

export const db = new Database(app, 'orders', {
engine: 'postgres',
version: '16',
});

export const stripeKey = new Secret(app, 'stripe-key', {});

export const web = new WebApp(app, 'web', {
handler: './src/server.ts',
port: 8080,
cpu: '0.25',
memory: 512,
bindings: { db, stripeKey },
});

src/server.ts — your application. The bindings you declared in infra.ts are usable directly; the runtime adapter wires them up.

import { createServer } from 'node:http';
import { db, stripeKey } from '../infra.js';

const port = Number(process.env.PORT ?? 8080);
createServer(async (req, res) => {
const key = await stripeKey.value();
const rows = await db.query('SELECT id, amount FROM orders ORDER BY id LIMIT 50');
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ apiKey: key.slice(0, 6) + '…', rows }));
}).listen(port);

Deploy

npx sail deploy --yes

This is fully idempotent. Re-running adopts existing resources and only rolls what changed.

The first deploy provisions the Database (RDS takes ~5–10 min), the Secret, builds the WebApp container, pushes it to ECR, and stands up an ECS Express service. You get back a public URL.

To deploy to the Sprintsail Runtime instead:

npx sail deploy --target sprintsail-runtime:my-cluster --yes

(See Sprintsail Runtime for what a cluster needs.)

Migrate between targets

This is the workflow the SDK is built around — the no-lock-in story.

npx sail migrate \
--from aws:us-east-1 \
--to sprintsail-runtime:my-cluster \
--yes

sail migrate walks the source state, provisions equivalents on the destination, then copies stateful data — RDS rows to CloudNativePG via an in-cluster pg_dump | psql Job, Secrets Manager values into sealed-secrets, S3 objects into MinIO — and generates a cutover script for your downstream consumers.

See The sail migrate model and the AWS → Runtime tutorial.