Self-hosting Langflow behind a public static site


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


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.


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.


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.


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.


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.


After deploy or Langflow version bump, verify in Playground:

  1. Ingest (datastax-astra-ingest): --limit 3 batch smoke (batch post )
  2. Chat (datastax-astra-chat): curl smoke test with streaming
  3. 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).


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.


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.

×
Page views: