Skip to main content

Overview

Chatwoot is an open-source customer engagement platform — an embeddable live-chat widget, a shared email inbox, and an omni-channel agent desk in one application. This template deploys the Community Edition (-ce, MIT core): a Rails web tier serving the dashboard, REST API, widget, and WebSockets on port 3000, a Sidekiq worker for background jobs and migrations, a bundled single-node Redis, and a highly available pgvector-capable PostgreSQL cluster. Attachments go to a persistent volume or to object storage.

Architecture

  • Chatwoot web — A stateful workload running the official image, serving the agent dashboard, the REST and public APIs, the chat widget, and the ActionCable WebSocket endpoint on port 3000. Scale it with chatwoot.replicas.
  • Chatwoot worker — A standard workload running Sidekiq from the same image. It also runs the database migrations and the first-run bootstrap on boot, so it is a fixed single replica; background throughput scales through worker.concurrency instead.
  • PostgreSQL (HA, default) — The postgres-highly-available template as a subchart: 3× Patroni PostgreSQL 17.5, 3× etcd, and a HAProxy leader endpoint Chatwoot connects through. Its image ships pgvector, which Chatwoot’s schema requires.
  • PostgreSQL (dev/lightweight, optional) — The single-instance postgres template instead, on a pgvector image.
  • Redis — A bundled single-node stateful workload with AOF persistence and password authentication, carrying the Sidekiq queues, the ActionCable pub/sub channel that drives every live update, the cache, and the one-time install-onboarding flag.

What Gets Created

  • Stateful Chatwoot Web Workload{release}-chatwoot on port 3000, at chatwoot.replicas replicas, with /api as its readiness probe and /health as its liveness probe.
  • Standard Chatwoot Worker Workload{release}-chatwoot-worker, a fixed single replica running Sidekiq, migrations, and the first-run bootstrap. It has no inbound access and no ports.
  • Stateful Redis Workload{release}-chatwoot-redis on port 6379, pinned to one node, reachable only from inside the GVC.
  • Database Workloads — HA mode: a stateful Patroni PostgreSQL workload, a stateful etcd workload, and a HAProxy leader-routing workload. Single-instance mode: one stateful PostgreSQL workload.
  • Volume Sets{release}-chatwoot-redis-vs for the Redis AOF file at /data, {release}-chatwoot-storage for local attachments at /app/storage (only when storage.type is local), plus the database subchart’s volumes.
  • Secrets — A template-created dictionary secret ({release}-chatwoot-creds) holding the bundled Redis password, two opaque start-script secrets for the web and worker containers, and the database credentials from the subchart. Your prerequisite keys secret is not created by the template — see Prerequisites.
  • Identity & Policy — One identity shared by the web and worker workloads, and a policy granting it reveal on exactly the secrets those workloads read: the database credentials, the two start scripts, the Redis credentials secret, your prerequisite secret, and — when configured — the S3 key and SMTP secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy.
This template does not create a GVC. You must deploy it into an existing GVC.

Prerequisites

Chatwoot signs its sessions and encrypts sensitive columns with four keys that you supply through a dictionary secret created before installing. The values are never passed through Helm values.
1

Create the keys secret

All four keys are required. Generate them with openssl:
Set secrets.name to the secret’s name (default my-chatwoot-secrets). Secrets are org-level, so no GVC flag is involved.
2

Back the values up

Store a copy somewhere safe, outside Control Plane. All four keys are write-once for the life of the installation.
3

Change the bundled credentials

Change the database password (postgresHA.postgres.password or postgres.config.password) and the Redis password (redis.auth.password) from their placeholder defaults before installing. Both seed their component on first boot and are not updated by later value edits.
Create the keys secret before installing — the workloads reference it by name, and a missing secret pauses the deployment indefinitely, which looks like a broken install. The keys are also write-once: rotating SECRET_KEY_BASE logs out every user, and rotating any ACTIVE_RECORD_ENCRYPTION_* key makes stored two-factor secrets undecryptable, locking out every agent who enabled MFA.
Everything else works with the defaults. Three optional features need their own setup first:
  • Object-storage attachments — an existing bucket plus either a Control Plane cloud account (AWS S3) or a static-key secret (S3-compatible servers). See Attachment Storage. Required if you plan to run more than one web replica.
  • Authenticated SMTP — a dictionary secret holding SMTP_USERNAME and SMTP_PASSWORD. See Email.
  • Database backups — a bucket and access setup for one of the supported providers. See Backing Up.
Once your keys secret exists, install the template using your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

First Run

Chatwoot ships no default account. The first browser session to reach the endpoint runs the onboarding wizard and creates the super admin.
1

Wait for the web workload to report ready

On the default HA database path a first install takes roughly eight minutes end to end: the Patroni cluster elects a leader, the worker then loads the schema and runs 158 migrations, and only then does the web tier pass its readiness probe. The single-instance database path is closer to three and a half minutes.
2

Complete the onboarding wizard

Browse to the canonical *.cpln.app endpoint of the {release}-chatwoot workload. It redirects to /installation/onboarding, where the form creates your account and the super admin user. Do this as soon as the workload is ready — the wizard is unauthenticated until it is completed.
3

Create an inbox

In the dashboard, add an inbox — a website widget for live chat, or an API channel for a custom integration. The widget snippet and the API channel identifier are issued there.
4

Configure email before inviting agents

Agent invitations, password resets, and email-channel replies are all delivered by mail. Set up SMTP before you invite your team.
Once an inbox exists, incoming visitor messages and agent replies travel over the WebSocket connection: an agent sees a new message without refreshing, and the widget receives the agent’s reply live.

Configuration

The default values.yaml for this template:

Chatwoot Web

  • chatwoot.image — The Community Edition image, used by both the web and worker workloads. Pin a released -ce tag.
  • chatwoot.replicas — Web-tier replica count. Anything above 1 requires object storage; see Scaling.
  • chatwoot.frontendUrl — The public base URL Chatwoot puts in the widget snippet, email links, and callbacks, including the https:// scheme. Leave it empty to derive it from the web workload’s canonical endpoint; set it when you serve Chatwoot on a custom domain. The worker advertises the same URL, so mailer links always point at the web tier.
  • chatwoot.resources — CPU and memory bounds for the web container. The memory ceiling leaves headroom for the Rails boot path.

Sidekiq Worker

  • worker.concurrency — Number of background jobs Sidekiq processes in parallel. It also sets this process’s PostgreSQL connection-pool size.
  • worker.resources — CPU and memory bounds for the worker container.
The worker is a fixed single replica: it runs the database migrations and the first-run bootstrap, which are not safe to run concurrently on a fresh database. Scale background throughput with worker.concurrency instead.
The worker workload has no probes — Sidekiq exposes no port, and its failure mode is process exit, which restarts the container. It therefore reports ready as soon as it is scheduled, even while it is still migrating. Worker readiness is not a health signal; read its logs instead.

Attachment Storage

storage.type decides where uploaded files and images are kept.
Attachments are written to a persistent volume set mounted at /app/storage on the web workload, and survive restarts, redeploys, and upgrades under the same release name.
The volume is attached to the web workload only, so the Sidekiq worker cannot read attachments in this mode — attachment emails and ActiveStorage analyze and purge jobs cannot succeed. Local storage also works only with a single replica. Use object storage for production.

Scaling

chatwoot.replicas sets how many web replicas run. Replicas share the database and the bundled Redis, so a message received by one replica reaches a WebSocket client connected to another.
chatwoot.replicas above 1 requires storage.type s3 or s3-compatible. Local attachments live on a per-replica volume, so a file uploaded through one replica would 404 from another. The chart refuses to render the combination.
At two replicas, forcing a redeployment of the web workload served every request without a single HTTP error, and a live agent reply still reached the visitor’s socket while both replicas were serving.

Email

SMTP is off by default, and a default install works without it — the onboarding wizard creates the super admin with no email confirmation, and that account signs in with its password.
  • smtp.address — The relay hostname. Chatwoot reads SMTP_ADDRESS, not SMTP_HOST.
  • smtp.domain — The HELO domain. Empty omits it.
  • smtp.authenticationplain, login, or cram_md5. Leave it empty for an unauthenticated relay.
  • smtp.enableStarttlsAuto — Set false only for a plaintext relay such as a local mail catcher.
  • smtp.fromEmail — The From address on outbound mail.
  • smtp.auth.secretName — Optional dictionary secret with the relay credentials, created before installing:
Leave it empty for a relay that needs no authentication. When it is set, the Chatwoot identity is granted reveal on exactly that secret.
With SMTP disabled, Chatwoot falls back to sendmail, which is absent from the image, so nothing is delivered: agent invitations, password resets, and email-channel replies all fail. Configure smtp.* before inviting agents.

Access

  • publicAccess.enabled — Serve the dashboard, API, widget, and WebSockets on the auto-assigned *.cpln.app HTTPS endpoint (default). Because the widget and visitors live outside the GVC, public access is on by default. Set to false for an internal-only instance: external requests are refused at the edge, and in-GVC callers still reach it per internalAccess.
  • internalAccess.type — Controls which workloads can reach the Chatwoot web tier over the internal network:
The worker and the bundled Redis are never publicly reachable: the worker accepts no inbound traffic at all, and Redis accepts only same-GVC traffic.

Database

Enable exactly one of postgresHA (default) or postgres — the chart refuses to render with both or neither enabled. Chatwoot is wired to the active database automatically: the HAProxy leader endpoint in HA mode, or the single instance directly.
  • postgresHA (default) — 3× Patroni PostgreSQL 17.5 with etcd and a HAProxy leader endpoint. Its image ships pgvector natively, which is why it is the default. A first install on this path takes roughly eight minutes.
  • postgres — One PostgreSQL instance for lighter, non-production deployments. It reaches ready in a few minutes but has no failover.
In both modes, change the database password before installing (postgresHA.postgres.password / postgres.config.password) — it seeds the database on first boot and is not updated by later value edits.
Choose the database mode before installing — it cannot be switched later. Flipping postgresHA.enabled / postgres.enabled on a live release points Chatwoot at a different, empty database (a separate volume set, and a different PostgreSQL major version), so the app re-runs onboarding and your existing conversations are orphaned rather than migrated.
In single-instance mode, postgres.image must carry pgvector. Chatwoot’s schema runs CREATE EXTENSION "vector", which the stock postgres: image does not provide. The chart refuses to render against a stock postgres: image rather than letting the install fail later as a cryptic schema error — keep the default pgvector/pgvector:pg18 unless you supply your own pgvector build.
On the HA path the worker crash-loops a handful of times during the first install, logging PG::ConnectionBad … server closed the connection unexpectedly while Patroni elects a leader. This is expected and self-heals as soon as the leader accepts writes.

Redis

  • redis.image — The bundled Redis image.
  • redis.auth.password — Password for the bundled Redis, embedded in Chatwoot’s REDIS_URL. Change it before installing. Only letters, digits, -, and _ are accepted; other characters would corrupt the connection URL, and the chart rejects them.
  • redis.resources — CPU and memory bounds for the Redis container.
  • redis.volumeset.capacity — Volume size in GiB for the append-only file at /data. Persistence is on and eviction is disabled, so a queued job is never silently dropped.
Redis is required and runs as a single node by design, not as a simplification: it carries the pub/sub channel behind every live update, and Redis does not propagate published messages between replicas, so a multi-node topology would silently drop a share of the broadcasts that make the dashboard and widget update in real time. That single node has two operational consequences worth planning for.
Restart the web workload after any Redis restart. If the bundled Redis restarts — a redeploy, a reschedule, or an upgrade — the queues and the cache reconnect on their own, but the web tier’s real-time subscriber does not. Live updates then stop silently: nothing is logged, the health endpoint stays green, and agents and visitors simply stop seeing new messages until they refresh. Force a redeployment of the {release}-chatwoot workload to restore it, which takes about three to four minutes.
Expect a few minutes of 503s during a Helm upgrade. An upgrade restarts the bundled Redis, and the web readiness endpoint reports unhealthy without Redis, so every web replica leaves the load balancer until Redis is back — measured at roughly 32% failed requests across a three-minute window, even at two replicas. Upgrade during a quiet period. A redeployment of the web workload alone does not restart Redis and rolls cleanly.

Connecting

Managing the Installation

Chatwoot keeps instance-wide settings in its database rather than in environment variables, so they are managed in the application after install, not through Helm values. Sign in as the super admin and open https://<canonical>.cpln.app/super_admin. Under Settings you can change instance configuration, including ENABLE_ACCOUNT_SIGNUP, which controls whether visitors can create their own accounts. Self-serve signup is off on a fresh install — accounts are created by the onboarding wizard and by agent invitations until you turn it on.

Backing Up

Database backups are optional and disabled by default. They cover the PostgreSQL database — the conversations, contacts, inboxes, and users that make up your Chatwoot instance. Attachments are not included; in object-storage mode they live in your bucket, and in local mode on the attachment volume set. Enable backups with postgresHA.backup.enabled or postgres.backup.enabled (matching your database mode), and complete the storage setup for your provider before installing. The values below are shown under backup.* — set them within the enabled database block.
1

Create a bucket

Create an S3 bucket. Set backup.aws.bucket and backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an AWS IAM policy with the JSON below (replace YOUR_BUCKET), then set backup.aws.policyName to the policy’s name:
In HA mode, backup.mode selects logical (a scheduled pg_dump run by a cron workload) or wal-g (continuous WAL archiving). The single-instance mode takes scheduled logical dumps. backup.prefix is the key prefix within the bucket, and backup.*.schedule is a standard cron expression in UTC.

Important Notes

  • Create the keys secret before installingsecrets.name must point at an existing dictionary secret holding SECRET_KEY_BASE and the three ACTIVE_RECORD_ENCRYPTION_* keys. A missing secret pauses the deployment and the install looks broken until it exists.
  • All four keys are write-once — rotating SECRET_KEY_BASE logs out every user, and rotating an ACTIVE_RECORD_ENCRYPTION_* key makes stored two-factor secrets undecryptable. Back the values up outside Control Plane.
  • Complete the onboarding wizard right after install — the first browser session to reach the endpoint creates the super admin account, with no email confirmation.
  • Restart the web workload after any Redis restart — queues and cache recover on their own, but live updates stay dead, silently, until the web tier restarts.
  • A Helm upgrade restarts the bundled Redis and costs a few minutes of 503s — the web readiness probe fails while Redis is down, so replicas leave the load balancer. Upgrade during a quiet period.
  • Choose the database mode before installing — switching postgresHA and postgres on a live release points Chatwoot at a different, empty database.
  • In single-instance mode the database image must carry pgvector — keep postgres.image on a pgvector build; the chart refuses to render against a stock postgres: image.
  • chatwoot.replicas above 1 requires object storage — local attachments are per-replica and would 404 across replicas.
  • In local storage mode the worker cannot read attachments — the volume is attached to the web workload only, so attachment emails and ActiveStorage analyze and purge jobs fail. Use object storage for production.
  • AWS S3 is keyless only — a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only for S3-compatible endpoints.
  • With SMTP off, no mail is delivered — agent invites, password resets, and email-channel replies all fail. Configure smtp.* before inviting agents.
  • Do not scale the worker — it is a fixed singleton that also runs migrations and the first-run bootstrap. Scale background throughput with worker.concurrency.
  • Self-serve signup is managed in the app, not in values — turn it on at /super_adminSettings; it is off on a fresh install.
  • Enterprise features are not included — the -ce image omits SSO/SAML and SCIM, audit logs, agent capacity management, custom branding, SLA policies, and Captain AI. The core is MIT-licensed.
  • Data survives restarts and upgrades — conversations live in the database volume sets and local attachments in the storage volume set. Uninstalling deletes those volume sets and everything in them; your keys secret is yours and survives an uninstall.

External References

Chatwoot Self-Hosted Docs

Official self-hosting documentation

Environment Variables

Every setting the Chatwoot application reads from its environment

Community vs Enterprise

What the Community Edition image includes and excludes

Product Guides

Inboxes, channels, agents, and day-to-day usage

Chatwoot on GitHub

Source code and release notes

Chatwoot Template

View the source files, default values, and chart definition