Dark mode
Architecture & services

Architecture & services

Helpin Community runs as a single Docker Compose project. This page explains what each service does, which ports are exposed, and where your data lives, so you can plan capacity, networking, and backups.

How the pieces fit together

flowchart LR
  subgraph Public["Your HTTPS reverse proxy"]
    D["app.example.com"]
    H["help.example.com"]
    F["files.example.com"]
  end
  D --> FE["helpin-frontend :8085"]
  H --> HC["helpin-helpcenter :8086"]
  F --> G["garage :9005"]
  FE --> API["helpin-api"]
  HC --> API
  API --> PG[("postgres")]
  API --> R[("redis")]
  API --> N["nats"]
  API --> T["temporal"]
  API --> G
  W["helpin-worker"] --> PG
  W --> T
  API <--> AR["agent-runtime"]
  AR --> ARW["agent-runtime-worker"]
  ARW --> N

Only three ports are published, and all of them bind to 127.0.0.1 by default. Everything else talks over the private Compose network.

Services

Service

Role

Published port

helpin-frontend

Staff dashboard, plus the /api, /widget/*, and /sdk/* routes

8085 (dashboard)

helpin-helpcenter

Server-rendered public help center

8086

garage

S3-compatible object storage for attachments, images, and uploads

9005

helpin-api

Application API, WebSockets, and agent event projection

Internal

helpin-worker

Background jobs: indexing, sync, scheduled work

Internal

agent-runtime

Agent Runtime API that plans and executes agent runs

Internal

agent-runtime-worker

Executes agent turns and tool calls

Internal

postgres

PostgreSQL 17 with pgvector. Holds four databases: Helpin, Runtime, Temporal, and Temporal visibility

Internal

redis

Rate-limit counters and short-lived caches

Internal

nats

NATS JetStream for real-time events between services

Internal

temporal

Durable workflow engine for long-running jobs

Internal

helpin-migrate

One-shot job that applies database migrations before the API starts

temporal-schema, temporal-namespace

One-shot jobs that prepare Temporal

Note: One-shot jobs exit with code 0 when they succeed. That's expected, not a crash.

Built-in HTTPS proxy

On a public server with built-in HTTPS (the default), one more service runs, from compose.proxy.yaml:

Service

Role

Published port

caddy

Terminates HTTPS, gets and renews certificates, and routes the three public hostnames

80, 443 (TCP and UDP)

Caddy connects to helpin-frontend, helpin-helpcenter, and garage over a small private edge network, from a fixed address (EDGE_PROXY_IP). Helpin trusts forwarded client details from that address only. Its certificates live in the caddy_data and caddy_config volumes, which helpin backup includes. Installations that use their own proxy don't run this service or create the edge network.

Pinned infrastructure versions

Every image in a published bundle is pinned by digest, so an installation never changes underneath you.

Component

Version

PostgreSQL + pgvector

17 + 0.8.6

Redis

7.2.16

NATS

2.14.7

Temporal

1.32.0

Garage

2.3.0

Changing a version variable in .env does not upgrade Helpin. Use helpin upgrade, which verifies a tested upgrade path first.

Where your data lives

Volume

Contents

postgres_data

All four PostgreSQL databases

garage_data

Object storage metadata and files

redis_data

Redis persistence

nats_data

JetStream streams

execution_workspaces

Agent execution scratch space, checkouts, and published artifacts

Your .env file holds generated secrets and encryption keys. Stored credentials, such as AI keys, Git tokens, and SMTP passwords, can only be decrypted with those keys. Always back up .env together with the volumes. helpin backup does this for you.

What never leaves your server

  • There is no telemetry. Error reporting is off unless you set SENTRY_DSN yourself.

  • The chat widget snippet uses your own URLs and sends no analytics to Helpin.

  • AI requests go only to the providers you configure, using your own keys.

Network boundaries

Expose only your reverse proxy. Never publish PostgreSQL, Redis, NATS, Temporal, Agent Runtime, or the storage console to the internet, and never route /api/internal/ publicly. The public widget hostname only needs /widget/* and /sdk/*.

The single-node bundle has no storage redundancy. For fault tolerance, use off-host backups or an external, replicated S3-compatible store.

Was this article helpful?