App Deployment
What It Is
Section titled “What It Is”Flect apps are containerized services (Docker images) deployed to Nomad. Each app gets a public HTTPS hostname (<name>-<id>.up.flect.run) and can be bound to databases and KV stores via flect.toml.
Workflow
Section titled “Workflow”1. Build Docker image2. Push to a registry (ghcr.io, Docker Hub, etc.)3. flect app deploy <name> --image <img>:<tag> OR configure flect.toml → flect app deployCreate & Deploy
Section titled “Create & Deploy”# Create app recordflect app create my-app
# Deploy specific imageflect app deploy my-app --image ghcr.io/you/my-app:1.0.0
# With flect.toml in current directoryflect app deployflect.toml
Section titled “flect.toml”[app]name = "my-app"image = "ghcr.io/you/my-app:1.0.0"port = 3000 # container port to exposememory = 256 # MBcpu = 256 # MHzregion = "eu"
[[databases]]binding = "DB"name = "my-db"
[[kv]]binding = "CACHE"name = "my-kv"Injected Environment Variables
Section titled “Injected Environment Variables”Flect automatically injects these at deploy time:
| Variable | Description |
|---|---|
DB_URL |
libsql HTTP endpoint (from [[databases]] binding) |
CACHE_URL |
redis:// URL (from [[kv]] binding) |
CACHE_PREFIX |
key prefix (empty — proxy handles isolation) |
FLECT_TOKEN |
proxy auth token — do not override |
PORT |
port to listen on (default: 3000) |
Multiple bindings use the binding name: USERS_DB_URL, SESSION_STORE_URL, etc.
Dockerfile Requirements
Section titled “Dockerfile Requirements”FROM node:22-alpineWORKDIR /app
COPY package*.json ./RUN npm ci --omit=dev
COPY dist/ ./dist/EXPOSE 3000
CMD ["node", "dist/index.js"]Must:
- Listen on
$PORT(or 3000) - Respond to
GET /healthzwith 200 (Traefik health check) - Bundle all dependencies (no workspace symlinks in the image)
Using @flect/sdk
Section titled “Using @flect/sdk”import { Hono } from 'hono'import { db, kv } from '@flect/sdk'
const app = new Hono()
app.get('/healthz', (c) => c.json({ ok: true }))
app.get('/users', async (c) => { const rows = await db().execute('SELECT * FROM users') return c.json({ users: rows.rows })})
app.get('/cache/:key', async (c) => { const val = await kv().get(c.req.param('key')) return c.json({ value: val })})
export default apptsup Config for Bundled Build
Section titled “tsup Config for Bundled Build”export default { entry: ['src/index.ts'], format: ['cjs'], platform: 'node', bundle: true, noExternal: [/.*/], // bundle all deps — required for ioredis (CJS)}GitHub Actions CI/CD
Section titled “GitHub Actions CI/CD”name: Deployon: push: branches: [main]
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Build & push image run: | echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin docker build --platform linux/amd64 -t ghcr.io/${{ github.repository }}:${{ github.sha }} . docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Deploy to Flect run: | npm install -g @flect/cli flect app deploy my-app --image ghcr.io/${{ github.repository }}:${{ github.sha }} env: FLECT_TOKEN: ${{ secrets.FLECT_SERVICE_TOKEN }} FLECT_API: https://api.flect.cloudGenerate a service token for CI:
flect token create ci-deploy --org my-orgManagement
Section titled “Management”flect app list # list all appsflect app get <name> # details + URLflect app logs <name> # tail logsflect app restart <name> # restartflect app scale <name> --replicas 2flect app delete <name>Custom Domain
Section titled “Custom Domain”flect app domain add <name> docs.mycompany.com# Then add CNAME: docs.mycompany.com → <name>-<id>.up.flect.run