Chatwoot is an open-source alternative to Intercom and Zendesk — live chat, shared inboxes, email, social channels, and a help center, all self-hosted. Like most serious Rails applications, it isn't a single process: you run a web server, background workers (Sidekiq), PostgreSQL, and Redis. Get the topology right and it runs smoothly.
This guide deploys Chatwoot for production.
Chatwoot's architecture
| Component | Role |
|---|---|
| Rails web | HTTP API, dashboard, widget serving |
| Sidekiq worker | Background jobs: emails, webhooks, automations |
| PostgreSQL | Primary datastore (conversations, contacts) |
| Redis | Sidekiq queue, ActionCable (real-time) |
| Object storage | Attachments and uploads |
Web and worker run from the same image with different commands.
Step 1: Provision PostgreSQL and Redis
On [PandaStack](https://dashboard.pandastack.io), create:
- A managed PostgreSQL (Chatwoot requires the
pgvector-capable setup for newer features; standard PostgreSQL 14/16 works for core functionality). - A managed Redis (used for both Sidekiq and real-time ActionCable).
Step 2: Generate the secret key base
Chatwoot signs sessions and cookies with SECRET_KEY_BASE:
openssl rand -hex 64Step 3: Build the environment
Key variables (secrets stored as secret env vars):
SECRET_KEY_BASE=<generated>
FRONTEND_URL=https://support.example.com
RAILS_ENV=production
NODE_ENV=production
# PostgreSQL
POSTGRES_HOST=<managed-host>
POSTGRES_PORT=5432
POSTGRES_DATABASE=chatwoot
POSTGRES_USERNAME=<user>
POSTGRES_PASSWORD=<password>
# Redis
REDIS_URL=redis://:<password>@<redis-host>:6379
# Email (inbound + outbound)
MAILER_SENDER_EMAIL=support@example.com
SMTP_ADDRESS=<smtp-host>
SMTP_PORT=587
SMTP_USERNAME=<user>
SMTP_PASSWORD=<password>
# Object storage for attachments
ACTIVE_STORAGE_SERVICE=s3_compatible
STORAGE_BUCKET_NAME=<bucket>
STORAGE_ACCESS_KEY_ID=<key>
STORAGE_SECRET_ACCESS_KEY=<secret>
STORAGE_ENDPOINT=<endpoint>
STORAGE_REGION=<region>FRONTEND_URL must match your public HTTPS domain — it's used in emails, the chat widget, and webhooks.
Step 4: Prepare the database
Run the one-time database setup before first boot, as a pre-deploy step using the Chatwoot image:
bundle exec rails db:chatwoot_prepareThis creates and migrates the schema. On subsequent deploys, run only migrations:
bundle exec rails db:migrateStep 5: Deploy web and worker
Use the official image for both apps:
FROM chatwoot/chatwoot:latestPin a version in production.
Web app:
bundle exec rails s -p 3000 -b 0.0.0.0
# expose port 3000Worker app (same image):
bundle exec sidekiq -C config/sidekiq.yml
# no inbound portBoth share the same environment variables and connect to the same PostgreSQL/Redis.
Steps on PandaStack:
- 1Push the repo (or reference the image) to GitHub.
- 2Create the web container app; link managed PostgreSQL, set all env vars, set the migrate pre-deploy hook, expose port 3000, add the custom domain (SSL automatic).
- 3Create the worker container app from the same image with the Sidekiq command and the same env vars — no public port.
Chatwoot uses ActionCable (websockets) for real-time chat. PandaStack's Kong ingress passes websocket upgrades through, so live conversations update instantly.
Step 6: First-run and channels
Visit your domain and create the super-admin account. Then:
- Create an inbox (website live chat, email, or a social channel).
- Grab the website widget snippet and embed it on your site:
<script>
(function(d,t) {
var BASE_URL="https://support.example.com";
var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=BASE_URL+"/packs/js/sdk.js"; g.async=true;
s.parentNode.insertBefore(g,s);
g.onload=function(){
window.chatwootSDK.run({ websiteToken: "<token>", baseUrl: BASE_URL });
};
})(document,"script");
</script>- Invite agents and set up teams, automations, and canned responses.
Scaling considerations
| Lever | Effect |
|---|---|
| Web replicas | Handle more concurrent dashboard/widget traffic |
| Sidekiq replicas | Faster background job processing under load |
| PostgreSQL tier | Conversation volume drives storage and connections |
| Redis | Keep healthy — both queues and real-time depend on it |
For busy support teams, scale web and Sidekiq independently. Keep the worker always-on (don't scale-to-zero) so jobs like outbound emails and automations don't stall.
Operating tips
- Back up PostgreSQL — it holds every conversation and contact. Scheduled backups cover it.
- Object storage for attachments — not container disk.
- Inbound email needs proper MX/forwarding setup if you use email channels.
- Keep
FRONTEND_URLstable and pin image versions; run migrations on upgrades.
References
- [Chatwoot documentation](https://www.chatwoot.com/docs/)
- [Chatwoot environment variables](https://www.chatwoot.com/docs/self-hosted/configuration/environment-variables)
- [Chatwoot Docker setup](https://www.chatwoot.com/docs/self-hosted/deployment/docker)
- [Chatwoot Docker image](https://hub.docker.com/r/chatwoot/chatwoot)
---
Chatwoot gives you a full support suite you own end to end — the recipe is a web app, a Sidekiq worker, managed PostgreSQL, and Redis. PandaStack provisions the data stores, injects the connection, and passes websockets through for real-time chat. Stand up your help desk at [dashboard.pandastack.io](https://dashboard.pandastack.io).