# Deployment

> Build and deploy in one command. Functions run on Lambda, SPA assets on the CDN, plus schema migrations, secrets, and rolling back.

`helix deploy` builds your project and ships it to the Helix platform in one command. Functions run as a single AWS Lambda per project with an internal router, SPA assets are served from S3 through CloudFront, and the app is live at `https://{project-id}.helix-app.ai`. The first deploy provisions the project and records its ID; later deploys update it in place.

## The build pipeline

`helix build` produces the deployable output:

1. **Reads `helix.config.ts`.** Validates the project ID, workspace ID, auth aliases, and database schema.
2. **Scans `functions/`.** Builds the route map from the file system and identifies scheduled and app-trigger functions.
3. **Bundles all functions into one entry point.** esbuild compiles every handler into a single Lambda-compatible bundle with an internal router that dispatches each request to the correct handler.
4. **Extracts configs.** Reads the `defineFunction`, `defineSchedule`, and `defineAppTrigger` configs from each file to build the deployment manifest (schemas, trigger configs, routes).
5. **Builds the SPA.** Runs `vite build` if `app/` exists.
6. **Generates the manifest.** A JSON file describing all functions, routes, triggers, schemas, and assets.

```
.output/
├── manifest.json          # Deployment descriptor
├── lambda/
│   └── handler.js         # Single bundled entry point with internal router
└── public/                # Vite build output (if a SPA exists)
    ├── index.html
    └── assets/
```

## What helix deploy does

1. Runs `helix build` if the project isn't already built.
2. Uploads the Lambda bundle, creating or updating the project's single Lambda function.
3. Uploads SPA assets to the project's S3 bucket.
4. Sends the Drizzle schema to the migration API, which runs Drizzle Kit against the production database. The CLI never connects to the production database directly.
5. Registers triggers: cron schedules via EventBridge, app-trigger webhooks via the Helix trigger API.
6. Updates routing so function requests reach the new Lambda through API Gateway.
7. Activates the deployment. Traffic switches to the new version.

On the first deploy, the CLI provisions the project, records its ID, and prints the project ID and live URL. It asks for a workspace if you haven't set one.

## Deploy options

```bash
# Deploy the project in the current directory
helix deploy

# Name the project on its first deploy, when it gets provisioned
helix deploy --name "Deal desk"
```

`--name` is the only flag `helix deploy` takes, and it applies to the first deploy only, when the project is provisioned. Later deploys upload a new artifact to the project already named in `helix.config.ts`.

:::roadmap{title="Not available yet"}
Selecting a target with `--config`, skipping migrations with `--skip-db`, and validating a build with `helix build --validate` all belong to the platform design. The shipped CLI has no `--config` flag and no `helix build` command. Deploy targets come from the `projectId`, `workspaceId`, and `environment` in `helix.config.ts`, which `helix project set`, `helix workspace set`, and `helix env set` write for you.
:::

Each target (staging, production) lives in its own config file pointing at its own workspace and project, so a deploy always lands where the file says. See [configuration](/documentation/concepts/configuration/) and the [deploy command reference](/documentation/reference/cli/deploy/).

## Production URL and routing

All traffic arrives at CloudFront on the project's subdomain and routes by content type:

```
https://{project-id}.helix-app.ai/
├── /                      → SPA (index.html)
├── /about                 → SPA (client-side route)
├── /api/v1/users          → Function
├── /webhooks/shopify      → Function (app trigger)
└── /assets/main-abc123.js → Static asset (CDN)
```

Route priority matches the dev server:

1. **Function routes.** Exact match against the manifest's route table, forwarded through API Gateway to the project's Lambda.
2. **Static files.** SPA assets in S3, served from the CloudFront cache.
3. **SPA fallback.** `index.html` from S3 for all unmatched paths.

Custom domains are platform design, so a project is served on its generated Helix app URL and nowhere else. See [limits and roadmap](/documentation/reference/limits/).

## Roll back

To restore a previous version, deploy again from an earlier commit of your project: a deploy always ships the complete bundle, so redeploying an older state fully replaces the current one.

:::roadmap
A `helix rollback` command and preview deploys (`--preview`) appear in the platform design but aren't confirmed in the shipped CLI yet.
:::

## Variables and secrets

:::roadmap{title="Not available yet"}
The shipped CLI has no `helix vars` command, and `helix.config.ts` accepts no `config` block. Third-party credentials are handled by [authentication aliases](/documentation/guides/connected-services/), which is the shipped mechanism and keeps secrets out of code. Everything below describes the platform design.
:::

Secrets and target-specific values that aren't auth credentials would be stored per project, managed via the CLI or dashboard:

```bash
helix vars set WEBHOOK_SECRET abc123
helix vars list
helix vars remove WEBHOOK_SECRET

# Pull vars into a local .env file for development
helix vars pull .env.local
```

Variables live on the project rather than in the config file: the platform stores the values and a deploy injects them.

They would be read two ways. In config, a `requireEnv` thunk resolves the value when the CLI loads the config, so a deploy fails fast if it's missing:

```typescript
// helix.production.config.ts
import { defineConfig, requireEnv } from '@trayai/helix-sdk';

export default defineConfig({
  workspaceId: '9e8d7c6b-5a4f-3e2d-1c0b-9a8f7e6d5c4b',
  projectId: '1f2e3d4c-5b6a-7980-1234-567890abcdef',

  config: {
    internalSecret: () => requireEnv('INTERNAL_SECRET'), // fails fast, never committed
  },
});
```

In functions, read `process.env` directly:

```typescript
// functions/api/v1/flags.get.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  return { newUi: process.env.FEATURE_FLAG_NEW_UI === 'true' };
});
```

## On-premise deployment

:::roadmap{title="On-premise deployment"}
Helix is designed to eventually support on-premise deployment: a Kubernetes-based serverless runtime replaces AWS Lambda, and the database, KV store, and auth proxy run inside your infrastructure. The developer experience stays the same, with the same project structure and the same deploy command:

```bash
helix deploy --target on-prem --cluster my-cluster.internal
```

Project code doesn't change; only the deployment target does.
:::

---

Canonical: https://helix.tray.ai/documentation/guides/deployment/
Any link on this page is available as markdown by appending .md to its URL.
Full corpus: https://helix.tray.ai/documentation/llms-full.txt