From 37b5067b886243dcacef8bd2691fea8e318b24fe Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 06:12:06 +0000 Subject: [PATCH] Add Dockerfile + fly.toml for single-host Fly deploy Multi-stage Dockerfile compiles TypeScript in a build tier then ships only dist/ + production node_modules to a slim runtime image. tini handles signal forwarding so WebSocket sessions close cleanly when Fly redeploys. fly.toml runs both the HTTP webhook and the Twilio Media Stream WS on one always-on shared-cpu machine. auto_stop_machines is off because voice calls can't tolerate the cold-start delay scale-to- zero introduces. Health check hits /health every 30s. --- .dockerignore | 14 ++++++++++++++ Dockerfile | 39 +++++++++++++++++++++++++++++++++++++++ fly.toml | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 fly.toml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..82a62b8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +node_modules +dist +.git +.github +.vercel +.env +.env.* +*.log +coverage +tests +docs +README.md +*.md +public diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1c959ce --- /dev/null +++ b/Dockerfile @@ -0,0 +1,39 @@ +# Multi-stage build for the CoTrackPro Voice Center. +# Build tier compiles TypeScript; runtime tier ships only what's needed +# to run the compiled output. Keeps the final image under ~200MB. + +# ── Build stage ────────────────────────────────────────────────────────── +FROM node:20-alpine AS build +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY tsconfig*.json ./ +COPY src ./src +COPY api ./api +COPY scripts ./scripts +RUN npm run build + +# Prune devDependencies so the runtime stage only copies what it needs. +RUN npm prune --omit=dev + +# ── Runtime stage ──────────────────────────────────────────────────────── +FROM node:20-alpine AS runtime +WORKDIR /app + +# tini is a tiny init that forwards signals correctly — important for +# clean WebSocket shutdowns when Fly redeploys the machine. +RUN apk add --no-cache tini + +COPY --from=build /app/package*.json ./ +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/dist ./dist + +ENV NODE_ENV=production +ENV PORT=8080 +EXPOSE 8080 + +USER node +ENTRYPOINT ["/sbin/tini", "--"] +CMD ["node", "dist/index.js"] diff --git a/fly.toml b/fly.toml new file mode 100644 index 0000000..6f02b1e --- /dev/null +++ b/fly.toml @@ -0,0 +1,43 @@ +# fly.toml — single-host deployment for the CoTrackPro Voice Center. +# +# Both the HTTP webhook (/call/incoming, /health, /records, …) and the +# Twilio Media Stream WebSocket (/call/stream) run on this one Fly +# machine. SERVER_DOMAIN env var should match the public hostname Fly +# assigns (default: .fly.dev). +# +# `auto_stop_machines = false` keeps the machine always-on — voice calls +# can't tolerate the ~30s cold start that scale-to-zero would introduce. + +app = "cotrackpro-talk" +primary_region = "ord" + +[build] + +[env] + NODE_ENV = "production" + PORT = "8080" + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = false + auto_start_machines = true + min_machines_running = 1 + processes = ["app"] + + # Health check used by Fly's load balancer to decide whether to route + # traffic. /health is a tiny JSON endpoint; 5s timeout is generous. + [[http_service.checks]] + interval = "30s" + timeout = "5s" + grace_period = "30s" + method = "GET" + path = "/health" + +# Single shared-CPU machine. Plenty for a few concurrent calls. Bump +# to performance-1x or higher (memory_mb 1024+) if you start running +# into OOM under load — call sessions hold a few hundred KB each. +[[vm]] + cpu_kind = "shared" + cpus = 1 + memory_mb = 512