DEV Community

Cover image for Deploying Chatwoot – Open-Source Customer Support Platform
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya • Originally published at docs.vultr.com

Deploying Chatwoot – Open-Source Customer Support Platform

Chatwoot is an open-source customer engagement platform unifying live chat, email, and social/messaging conversations into a shared inbox, with real-time visitor tracking and team collaboration. This guide deploys it via Docker Compose with PostgreSQL, Redis, Sidekiq for background jobs, and Traefik for TLS, then walks through creating and testing a live-chat inbox.

Prerequisites: a Linux server (4 vCPU / 8GB RAM minimum), Docker + Docker Compose, a domain A record (e.g. chatwoot.example.com), optional SMTP credentials (needed for email channel, password resets, notifications).


Set Up the Project

$ mkdir -p ~/chatwoot/{postgres,redis,storage,letsencrypt}
$ cd ~/chatwoot
Enter fullscreen mode Exit fullscreen mode
  • postgres — database files
  • redis — cache + background job queue data
  • storage — file uploads/attachments
  • letsencrypt — Traefik ACME certs

Generate a secret key:

$ openssl rand -hex 32
Enter fullscreen mode Exit fullscreen mode

Create the environment file:

$ nano .env
Enter fullscreen mode Exit fullscreen mode
DOMAIN=chatwoot.example.com
LETSENCRYPT_EMAIL=admin@example.com
FRONTEND_URL=https://chatwoot.example.com

SECRET_KEY_BASE=GENERATED_SECRET_KEY

POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DATABASE=chatwoot
POSTGRES_USERNAME=chatwoot
POSTGRES_PASSWORD=STRONG_POSTGRES_PASSWORD

REDIS_URL=redis://redis:6379

RAILS_ENV=production
NODE_ENV=production
RAILS_LOG_TO_STDOUT=true

ACTIVE_STORAGE_SERVICE=local

# Optional: SMTP configuration for email
# SMTP_ADDRESS=smtp.example.com
# SMTP_PORT=587
# SMTP_USERNAME=SMTP_USERNAME
# SMTP_PASSWORD=SMTP_PASSWORD
# SMTP_DOMAIN=chatwoot.example.com
# SMTP_ENABLE_STARTTLS_AUTO=true
# MAILER_SENDER_EMAIL=noreply@chatwoot.example.com
Enter fullscreen mode Exit fullscreen mode

Replace the placeholders with your domain, email, DB password, and generated secret key. Uncomment and fill in the SMTP block if you need email channel support.


Deploy with Docker Compose

$ nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode
services:
  traefik:
    image: traefik:v3.7.0
    container_name: traefik
    restart: unless-stopped
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

  postgres:
    image: pgvector/pgvector:pg16
    container_name: chatwoot-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DATABASE}
      POSTGRES_USER: ${POSTGRES_USERNAME}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - "./postgres:/var/lib/postgresql/data"
    healthcheck:
      test: ["CMD", "pg_isready", "-d", "${POSTGRES_DATABASE}", "-U", "${POSTGRES_USERNAME}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:alpine
    container_name: chatwoot-redis
    restart: unless-stopped
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - "./redis:/data"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 3

  rails:
    image: chatwoot/chatwoot:v4.14.1
    container_name: chatwoot-rails
    restart: unless-stopped
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - "./storage:/app/storage"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.chatwoot.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.chatwoot.entrypoints=websecure"
      - "traefik.http.routers.chatwoot.tls.certresolver=letsencrypt"
      - "traefik.http.services.chatwoot.loadbalancer.server.port=3000"

  sidekiq:
    image: chatwoot/chatwoot:v4.14.1
    container_name: chatwoot-sidekiq
    restart: unless-stopped
    command: bundle exec sidekiq -C config/sidekiq.yml
    env_file:
      - .env
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    volumes:
      - "./storage:/app/storage"
Enter fullscreen mode Exit fullscreen mode
  • traefik — TLS termination, auto HTTP→HTTPS redirect, Let's Encrypt via ACME
  • postgres — primary datastore for conversations/users/config
  • redis — caching, sessions, Sidekiq queues
  • rails — the web app + WebSocket server on port 3000
  • sidekiq — background workers (email, webhooks, scheduled tasks)

Prepare the database and start everything:

$ docker compose run --rm rails bundle exec rails db:chatwoot_prepare
$ docker compose up -d
$ docker compose ps -a
$ docker compose logs
Enter fullscreen mode Exit fullscreen mode

Confirm all containers show Up, and rails/sidekiq/traefik logs show no repeated errors or connection refusals.


First-Run Setup

Visit https://chatwoot.example.com (a 502 on first load just means give it a few seconds and refresh):

  1. Enter Name, Company Name, Work Email, Password.
  2. Toggle the newsletter checkbox as you like.
  3. Finish Setup to land on the dashboard.

Create a Live Chat Inbox

  1. Click here to create an inbox on the welcome dashboard.
  2. Select Website as the channel.
  3. Enter Website Name and Website Domain.
  4. Set widget color, welcome heading, and tagline; optionally enable channel greeting.
  5. Create inbox, optionally assign agents, then Copy the embed snippet from the confirmation screen.

Test the Widget

  1. Settings → Inboxes, click the gear icon on your inbox, then Script to view the embed code.
  2. Open in CodePen for a live preview.
  3. Click the chat bubble, send a test message.
  4. Back in Chatwoot, Conversations shows the new message — open it and reply from the dashboard.

Next Steps

Chatwoot is running with persistent storage, background workers, and TLS. From here:

  • Add email and social channels (Facebook, WhatsApp, Instagram) alongside the website widget
  • Configure SMTP so password resets and notification emails actually send
  • Set up canned responses and team assignment rules to route conversations efficiently

For the full guide, visit the original article on Vultr Docs.

Top comments (1)

Collapse
 
topstar_ai profile image
Luis Cruz

I noticed that the docker-compose.yml file uses the pgvector/pgvector:pg16 image for PostgreSQL, which is a great choice for efficient full-text search capabilities. However, it's worth considering the implications of using a specialized PostgreSQL image, as it may require additional configuration and tuning for optimal performance. Have you considered using a more standard PostgreSQL image, such as the official postgres:16 image, and configuring full-text search capabilities separately? This could potentially simplify the deployment process and make it easier to manage and maintain the database.