Skip to content

Pre-GA Design Partner and Early Access only. Request access

Helix Docs

Deployment

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

For developers Updated Aug 5, 2026
View as Markdown

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

# 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.

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 and the deploy command reference.

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.

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.

Variables and secrets

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

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:

// 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:

// 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

Loading search…

Jump to a section

tab to move · esc to close