Back to Blog
Tutorial11 min read2026-06-30

How to Deploy Chatwoot Customer Support

Chatwoot is an open-source customer support and live chat platform. This guide deploys its web and worker processes with managed PostgreSQL and Redis for a production-ready help desk.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

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

ComponentRole
Rails webHTTP API, dashboard, widget serving
Sidekiq workerBackground jobs: emails, webhooks, automations
PostgreSQLPrimary datastore (conversations, contacts)
RedisSidekiq queue, ActionCable (real-time)
Object storageAttachments 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 64

Step 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_prepare

This creates and migrates the schema. On subsequent deploys, run only migrations:

bundle exec rails db:migrate

Step 5: Deploy web and worker

Use the official image for both apps:

FROM chatwoot/chatwoot:latest

Pin a version in production.

Web app:

bundle exec rails s -p 3000 -b 0.0.0.0
# expose port 3000

Worker app (same image):

bundle exec sidekiq -C config/sidekiq.yml
# no inbound port

Both share the same environment variables and connect to the same PostgreSQL/Redis.

Steps on PandaStack:

  1. 1Push the repo (or reference the image) to GitHub.
  2. 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).
  3. 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

LeverEffect
Web replicasHandle more concurrent dashboard/widget traffic
Sidekiq replicasFaster background job processing under load
PostgreSQL tierConversation volume drives storage and connections
RedisKeep 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_URL stable 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).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also