Web service operations
The Web service hosts the TelWeb management dashboard (Next.js) behind a Caddy reverse proxy with automatic HTTPS. Caddy also reverse-proxies the SigNoz dashboard. Single instance, static IP.
- Overview
- Runbook
- Configuration
- Troubleshooting
Instance
| Field | Value |
|---|---|
| Network | Public static IP + private network |
| Scalable | No — single instance |
Containers
| Container | Image | Port(s) | Purpose |
|---|---|---|---|
voiceai-telweb | ${ECR_REGISTRY}/voiceai-telweb:${ECR_TAG} (Node 24-alpine) | 5050 | Next.js dashboard |
voiceai-caddy | caddy:2-alpine | 80, 443 | Reverse proxy + automatic Let's Encrypt TLS |
voiceai-otel-collector | otel/opentelemetry-collector-contrib:0.154.0 | — | Telemetry collector (host network) |
TelWeb and Caddy share a bridge network (web). The OTel collector runs in host network mode and TelWeb reaches it via host.docker.internal.
Caddyfile
The Caddyfile configures two virtual hosts:
${DOMAIN_TELWEB}— reverse-proxy totelweb:5050; HSTS, X-Frame-Options, gzip, static asset caching.${DOMAIN_SIGNOZ}— reverse-proxy to${OTEL_BACKEND_HOST}:${SIGNOZ_PORT}(default10.0.1.10:8080).
Database readiness
TelWeb entrypoint.sh waits until Postgres accepts TCP connections, then starts the standalone Next.js server. The container has a 40-second start period so first boot has time for the database wait — subsequent boots are faster.
Prisma migrate deploy, baseline db seed, and the delphi-setup registry CLI all run from the voiceai-db-migrate one-shot container on the Ops service. Ops init.sh runs that container during deploy; operators run ./delphi-setup.sh on the Ops host for interactive first-use and --apply-missing after releases — see First use (delphi-setup).
For v0.9.13+ upgrades, run release migrations on Ops before restarting long-running Web, API, Voice, Tasker, or Scaler services on the new tag.
Deploy and update
init.sh reads the bootstrap /opt/deployment/.env, fetches config from S3, loads SSM / Secrets Manager values into memory via fetch-env.sh, logs into ECR, and starts Compose.
cd /opt/services/web
./init.sh # full bring-up
./update.sh --ecr-tag v0.9.13 # roll a new image
./update.sh --restart-only # restart without pulling new images
Both init.sh and update.sh accept --skip-images, --skip-fetch, --dry-run.
TLS
Caddy obtains and renews Let's Encrypt certificates automatically for DOMAIN_TELWEB and DOMAIN_SIGNOZ. Certificates persist in the caddy-data named volume. No manual cert rotation is needed; verify DNS resolves to the static IP before relying on auto-issuance.
Health checks
| Container | Probe | Interval | Notes |
|---|---|---|---|
voiceai-telweb | wget --spider http://127.0.0.1:5050/ | 30s | 40s start period for migrations |
voiceai-caddy | wget --spider http://127.0.0.1:2019/config/ | 30s | Caddy admin endpoint |
TelWeb (Next.js)
| Name | Source | Scope | Default | Description |
|---|---|---|---|---|
DATABASE_URL | Secrets Manager | all | — | PostgreSQL connection string. |
DATABASE_SSL_MODE | SSM | all | disable | disable | require | verify-ca | verify-full. |
DATABASE_SSL_CA_BUNDLE_B64 | Secrets Manager | all | — | Base64 PEM CA for Prisma Postgres TLS; same rules as API (PgBouncer vs RDS). |
DATABASE_SSL_CA_FILE | env | all | /etc/ssl/database/ca.crt | Decoded DB CA path inside the TelWeb container. |
REDIS_HOST | SSM | all | — | Redis server address. |
REDIS_PASSWORD | Secrets Manager | all | — | Redis authentication. |
REDIS_TLS_ENABLED | SSM | all | false | Enable TLS for Redis client connections. |
REDIS_TLS_CA_BUNDLE_B64 | Secrets Manager | all | — | Base64 PEM CA for Redis TLS; optional for Amazon-signed endpoints. |
REDIS_TLS_CA_FILE | env | all | /etc/ssl/redis/ca.crt | Decoded Redis CA path inside the TelWeb container. |
NEXTAUTH_URL | SSM | all | — | Base URL used by NextAuth and password-expiry email links (Tasker reads the same value). |
NEXTAUTH_SECRET | Secrets Manager | all | — | Session encryption key. Must match across all consumers. |
SESSION_TIMEOUT | env | all | 1800000 | Session timeout in milliseconds for server cookies and client idle logout. |
PASSWORD_MAX_AGE_DAYS | env | all | 90 | Days until a password expires after last change. Tasker must use the same value. |
PASSWORD_WARNING_DAYS | env | all | 10 | Days before expiry to show in-app warnings and send Tasker reminder emails. |
AUTH_WITH_MICROSOFT | SSM | all | false | Enable Entra ID auth (requires the trio of MICROSOFT_ENTRA_ID_* vars). |
LEGAL_BANNER_HTML | SSM | all | — | Trusted operator-provided HTML shown as a public legal notice on unauthenticated TelWeb pages. Takes precedence over LEGAL_BANNER_TEXT. |
LEGAL_BANNER_TEXT | SSM | all | — | Plain-text legal notice shown on unauthenticated TelWeb pages when LEGAL_BANNER_HTML is unset. |
STRIPE_SECRET_KEY | Secrets Manager | all | — | Subscription billing. |
STRIPE_WEBHOOK_SECRET | Secrets Manager | all | — | Stripe webhook signature verification. |
DELPHI_PLATFORM_ROLE_ARN | SSM | all | — | AWS IAM role for Delphi platform features (replaces password-based setup). |
TELAPI_KEY | Secrets Manager | all | — | TelAPI key for the WebRTC phone component. |
ICE_SERVERS | SSM | all | — | Optional full ICE JSON override (otherwise derived from telproDomain in the session-token response). |
Authentication and session policy
TelWeb enforces account lockout, password expiry, and concurrent-session checks during sign-in. Defaults apply when the relevant env var is unset.
| Control | Default | How to configure |
|---|---|---|
| Failed credentials login lockout | 5 failed attempts in a 30 minute counting window. Locked accounts stay locked until the reset-password flow unlocks them. | Set LOGIN_MAX_ATTEMPTS_CREDENTIALS and LOGIN_LOCKOUT_MINUTES_CREDENTIALS on the Web service. |
| Microsoft Entra ID lockout policy | 10 failed attempts and 60 minutes for the reported lockout window. | Set LOGIN_MAX_ATTEMPTS_AZURE_AD and LOGIN_LOCKOUT_MINUTES_AZURE_AD on the Web service when Entra ID auth is enabled. |
| Password lifetime | 90 days; warning/reminder window defaults to 10 days. | Set PASSWORD_MAX_AGE_DAYS and PASSWORD_WARNING_DAYS on both Web and Tasker. The values must match so TelWeb enforcement and Tasker reminder emails use the same policy. |
| Concurrent sessions | 2 active sessions per user in production; unlimited outside production unless explicitly set. | Set MAX_CONCURRENT_SESSIONS on the Web service. Use a positive integer for a cap, or 0 / negative to disable the cap. |
| Abandoned session slot lifetime | Defaults to SESSION_TIMEOUT. | Set SESSION_HEARTBEAT_TTL on the Web service to reclaim unused session slots faster, but keep it between 60000 and SESSION_TIMEOUT. |
Before relying on an override, verify that the Web bundle exposes it:
/opt/services/common/show-env.sh --service web
After changing Web values, restart Web. After changing password-expiry values, restart both Web and Ops so Tasker picks up the matching policy:
cd /opt/services/web && ./update.sh --restart-only
cd /opt/services/ops && ./update.sh --restart-only
Caddy
| Name | Source | Scope | Default | Description |
|---|---|---|---|---|
DOMAIN_TELWEB | SSM | all | — | Primary dashboard domain. |
DOMAIN_SIGNOZ | SSM | all | — | Monitoring dashboard domain. |
OTEL_BACKEND_HOST | SSM | all | 10.0.1.10 | SigNoz host IP (proxy target). |
SIGNOZ_PORT | SSM | all | 8080 | SigNoz UI port. |
See Configuration model for how SSM / Secrets Manager / DB sources fit together.
Public legal notice
Set LEGAL_BANNER_TEXT or LEGAL_BANNER_HTML to show a notice above unauthenticated and public TelWeb pages, for example login, registration, or public auth pages. Leave both unset to hide the banner.
Use LEGAL_BANNER_TEXT for plain text. Use LEGAL_BANNER_HTML only for trusted operator-authored markup; it is rendered as HTML so the notice can include links or emphasis required by a deployment's legal wording.
| Symptom | Likely cause | Check |
|---|---|---|
| HTTPS broken / cert errors | Caddy ACME failure | docker compose logs voiceai-caddy; verify DNS for DOMAIN_TELWEB. |
| 502 Bad Gateway | TelWeb crash on boot | docker compose ps voiceai-telweb; tail TelWeb logs. |
| Slow first page load | First-boot migrations | Wait out the 40s start period; subsequent boots are fast. |
| Auth fails everywhere | NEXTAUTH_SECRET drift between API and TelWeb | Ensure both services pull the same value from Secrets Manager. |
| SigNoz dashboard 502 | Caddy cannot reach 10.0.1.10:8080 | curl http://10.0.1.10:8080 from the Web instance; check SigNoz health. |
| Email notifications not sending | Email is sent from the Ops (Tasker) service, not TelWeb | Check Tasker logs on the Ops instance for SMTP / SES errors. |
See also
- API operations — the LB-backed Fastify API TelWeb talks to.
- Configuration model — where each variable lives and how to change it.
- Monitoring in SigNoz — finding TelWeb traces, logs, dashboards, and alerts.