Astra Docs Chat is public on Cloudflare Pages. Langflow runs on a private instance: reachable from the Pages Function proxy, not from browsers directly.
This post covers the ops shape I use: Dockge/compose, PostgreSQL persistence, environment variables, CORS as belt-and-braces, and what you expose vs keep internal.
Series: Building Astra Docs Chat · Proxy pattern
Public chat: Astra Docs Chat
Topology ¶
Internet → jamieede.com (Hugo + Pages Functions)
↓ HTTPS + x-api-key (server-side only)
Langflow (private host / tunnel; URL not in HTML or JS)
↓
OpenAI (embeddings), DeepSeek (chat), Astra DB (vectors)
Visitors never receive the Langflow hostname in HTML or JS. DevTools on /astra-chat should show only same-origin /api/astra-chat (proxy verification
).
Ingest runs from your laptop via the batch ingest script , not from public visitors.
Docker stack (Dockge / compose) ¶
I run Langflow from langflow/dockge/compose.yaml:
services:
langflow:
image: langflowai/langflow:${LANGFLOW_IMAGE_TAG:-latest}
ports:
- "${LANGFLOW_HOST_PORT:-7860}:7860"
environment:
LANGFLOW_DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
LANGFLOW_CONFIG_DIR: /app/langflow
volumes:
- ${LANGFLOW_DATA_ROOT}/data:/app/langflow
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-trixie
volumes:
- ${LANGFLOW_DATA_ROOT}/postgres:/var/lib/postgresql/data
Langflow on port 7860 with persistent config under LANGFLOW_DATA_ROOT/data.
PostgreSQL 16 for Langflow’s internal database (flows, users, run history). Postgres stays on the Docker internal network by default; host port exposure is commented out.
Healthchecks: Langflow curl /health; Postgres pg_isready.
Langflow and Postgres healthy in Dockge; compose uses env placeholders, not committed secrets.
In Dockge: New Stack → point at this folder → copy .env.example to .env → Deploy.
Environment variables (.env.example) ¶
Copy to .env and change every value before deploy:
| Variable | Purpose |
|---|---|
LANGFLOW_DATA_ROOT |
Host path for bind mounts (/mnt/nvme/langflow) |
LANGFLOW_IMAGE_TAG |
Pin e.g. 1.4.0 instead of latest for reproducible upgrades |
LANGFLOW_HOST_PORT |
7860 |
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB |
Must match LANGFLOW_DATABASE_URL |
LANGFLOW_AUTO_LOGIN |
false for anything reachable beyond localhost |
LANGFLOW_SUPERUSER / LANGFLOW_SUPERUSER_PASSWORD |
Langflow UI admin |
LANGFLOW_CORS_ORIGINS |
["https://www.jamieede.com","https://jamieede.com"] |
Optional commented vars: LANGFLOW_LOG_LEVEL, LANGFLOW_REMOVE_API_KEYS, LANGFLOW_SSRF_PROTECTION_ENABLED.
CORS matters only if something calls Langflow from the browser. With the proxy pattern, it is defensive. Belt-and-braces if a future tool hits Langflow directly.
Do not commit .env. Provider keys (OpenAI, DeepSeek, Astra) belong in Langflow global variables inside the UI, not necessarily in compose env, unless you inject them at container start.
Network exposure ¶
| Expose to Cloudflare proxy / your IP | Keep internal |
|---|---|
POST /api/v1/run/{endpoint} |
Langflow UI (or restrict UI by VPN/IP) |
POST /api/v2/files/ (ingest script only) |
Postgres port |
| Health check for monitoring | Raw flow JSON exports with secrets |
If Langflow is on a home network, use Cloudflare Tunnel or reverse proxy with TLS. Set LANGFLOW_URL in Cloudflare Pages secrets to that HTTPS origin.
Ingest /api/v2/files/ does not need to be world-open if you restrict by network and only run batch jobs from trusted machines.
Langflow returns 403 without x-api-key on run endpoints. Rotate Langflow API keys independently of provider keys.
Layered secrets ¶
| Secret location | Holds |
|---|---|
| Cloudflare Pages | LANGFLOW_URL, LANGFLOW_API_KEY |
| Langflow global variables | OPENAI_API_KEY, DEEPSEEK_API_KEY, Astra token + endpoint |
.env (compose) |
Postgres password, Langflow superuser |
Compromise of Pages secrets lets an attacker run published flows as your site, but does not directly expose OpenAI billing unless the Langflow key can edit flows or read globals.
See Langflow RAG flows for which components consume which keys.
Flows to maintain ¶
After deploy or Langflow version bump, verify in Playground:
- Ingest (
datastax-astra-ingest):--limit 3batch smoke (batch post ) - Chat (
datastax-astra-chat): curl smoke test with streaming - Public path: preview URL
/astra-chat+ Network tab check
Back up ${LANGFLOW_DATA_ROOT}/data and ${LANGFLOW_DATA_ROOT}/postgres before major upgrades. Export important flows as JSON after meaningful edits (scrub secrets from exports).
Watch logs for 401 spikes (rotated keys) and ingest timeouts (300s per file in script).
When self-hosting is worth it ¶
Worth it: you already run homelab/Dockge, want visual RAG graphs, multiple flows (ingest + chat), provider flexibility (DeepSeek swap ).
Skip it: managed retrieval SaaS, single Worker calling Workers AI + Vectorize, team without ops appetite.
This project chose self-hosted Langflow to reuse the DataStax bundle template quickly: not because it is the only architecture.
Next in the series ¶
- Langflow RAG flows
- Batch ingest script
- Proxying Langflow from Cloudflare Pages Functions
- Building a streaming chat UI in Hugo : what visitors actually hit
Series index: Building Astra Docs Chat
Open Astra Docs Chat : the visible site is Hugo on Cloudflare; Langflow is the private engine behind one POST route.