Skip to main content

Docker Deployment

Deploy Chatwoot using Docker and Docker Compose for a containerized, portable deployment. This is ideal for development, testing, and production environments that use container orchestration.

Prerequisites

  • Docker Engine 20.10+
  • Docker Compose v2.0+
  • 4GB+ RAM available
  • 20GB+ disk space

Install Docker

# Ubuntu/Debian
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add your user to docker group
sudo usermod -aG docker $USER
newgrp docker

Install Docker Compose

# Docker Compose is included with Docker Desktop
# For Linux servers:
sudo apt-get install docker-compose-plugin

# Verify installation
docker compose version

Quick Start

1. Clone the Repository

git clone https://github.com/chatwoot/chatwoot.git
cd chatwoot

2. Configure Environment

Copy the example environment file:
cp .env.example .env
Edit .env with your configuration:
# Generate a secure secret key
SECRET_KEY_BASE=$(openssl rand -hex 64)

# Update these values in .env
SECRET_KEY_BASE=<your-generated-key>
FRONTEND_URL=http://localhost:3000
REDIS_PASSWORD=<secure-password>
POSTGRES_PASSWORD=<secure-password>

3. Start Services

docker compose up -d
Chatwoot will be available at http://localhost:3000.

Docker Compose Configuration

Here’s the production-ready docker-compose.yaml configuration:
version: '3'

services:
  rails:
    image: chatwoot/chatwoot:latest
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    ports:
      - '3000:3000'
    env_file: .env
    environment:
      - NODE_ENV=production
      - RAILS_ENV=production
      - INSTALLATION_ENV=docker
    depends_on:
      - postgres
      - redis
    volumes:
      - ./data/storage:/app/storage

  sidekiq:
    image: chatwoot/chatwoot:latest
    command: bundle exec sidekiq -C config/sidekiq.yml
    env_file: .env
    environment:
      - NODE_ENV=production
      - RAILS_ENV=production
    depends_on:
      - postgres
      - redis
    volumes:
      - ./data/storage:/app/storage

  postgres:
    image: pgvector/pgvector:pg16
    restart: always
    ports:
      - '5432:5432'
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=chatwoot
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

  redis:
    image: redis:alpine
    restart: always
    command: ["sh", "-c", "redis-server --requirepass \"$REDIS_PASSWORD\""]
    env_file: .env
    volumes:
      - redis:/data/redis
    ports:
      - '6379:6379'

volumes:
  postgres:
  redis:

Production Deployment

For production deployments with Nginx reverse proxy:

1. Docker Compose with Nginx

Create docker-compose.production.yml:
version: '3'

services:
  nginx:
    image: nginx:alpine
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
      - ./data/storage:/app/storage:ro
    depends_on:
      - rails

  rails:
    image: chatwoot/chatwoot:latest
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    env_file: .env
    environment:
      - NODE_ENV=production
      - RAILS_ENV=production
      - RAILS_SERVE_STATIC_FILES=false
      - INSTALLATION_ENV=docker
    depends_on:
      - postgres
      - redis
    volumes:
      - ./data/storage:/app/storage

  sidekiq:
    image: chatwoot/chatwoot:latest
    command: bundle exec sidekiq -C config/sidekiq.yml
    env_file: .env
    environment:
      - NODE_ENV=production
      - RAILS_ENV=production
    depends_on:
      - postgres
      - redis
    volumes:
      - ./data/storage:/app/storage

  postgres:
    image: pgvector/pgvector:pg16
    restart: always
    volumes:
      - postgres:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=chatwoot
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

  redis:
    image: redis:alpine
    restart: always
    command: ["sh", "-c", "redis-server --requirepass \"$REDIS_PASSWORD\""]
    env_file: .env
    volumes:
      - redis:/data/redis

volumes:
  postgres:
  redis:

2. Nginx Configuration

Create nginx.conf:
upstream backend {
  server rails:3000;
  keepalive 32;
}

map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

server {
  listen 80;
  server_name your-domain.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name your-domain.com;

  ssl_certificate /etc/nginx/ssl/fullchain.pem;
  ssl_certificate_key /etc/nginx/ssl/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!aNULL:!MD5;

  client_max_body_size 50M;

  location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 300s;
  }

  location /assets/ {
    alias /app/public/assets/;
    expires max;
    add_header Cache-Control public;
  }
}

3. Generate SSL Certificates

Using Let’s Encrypt:
# Install certbot
sudo apt-get install certbot

# Generate certificates
sudo certbot certonly --standalone -d your-domain.com

# Copy certificates
mkdir -p ssl
sudo cp /etc/letsencrypt/live/your-domain.com/fullchain.pem ssl/
sudo cp /etc/letsencrypt/live/your-domain.com/privkey.pem ssl/
sudo chown -R $USER:$USER ssl/

4. Start Production Stack

docker compose -f docker-compose.production.yml up -d

Database Setup

After starting the containers, prepare the database:
# Run migrations and seed data
docker compose exec rails bundle exec rails db:chatwoot_prepare

Using Pre-built Images

Chatwoot publishes official Docker images:
services:
  rails:
    image: chatwoot/chatwoot:latest  # Latest stable
    # or
    image: chatwoot/chatwoot:v4.0.0  # Specific version
    # or
    image: chatwoot/chatwoot:develop  # Development branch

Building Custom Images

To build your own image with customizations:

1. Review the Dockerfile

Chatwoot uses a multi-stage build:
# Pre-build stage
FROM ruby:3.4.4-alpine3.21 AS pre-builder

ARG BUNDLE_WITHOUT="development:test"
ARG RAILS_ENV=production
ARG NODE_OPTIONS="--max-old-space-size=4096 --openssl-legacy-provider"

ENV BUNDLE_PATH="/gems"
ENV BUNDLER_VERSION=2.5.16

# Install dependencies
RUN apk update && apk add --no-cache \
  build-base postgresql-dev git curl nodejs npm

WORKDIR /app

# Install Ruby dependencies
COPY Gemfile Gemfile.lock ./
RUN bundle install

# Install Node dependencies
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm i

# Copy application code
COPY . /app

# Precompile assets
RUN SECRET_KEY_BASE=precompile_placeholder \
    bundle exec rake assets:precompile

# Final stage
FROM ruby:3.4.4-alpine3.21

RUN apk add --no-cache \
  postgresql-client imagemagick vips

COPY --from=pre-builder /gems/ /gems/
COPY --from=pre-builder /app /app

WORKDIR /app
EXPOSE 3000

2. Build Custom Image

# Build the image
docker build -t my-chatwoot:latest .

# Use in docker-compose
services:
  rails:
    image: my-chatwoot:latest
    build:
      context: .
      dockerfile: docker/Dockerfile

Environment Variables

Key environment variables for Docker deployment:
# Application
SECRET_KEY_BASE=<64-char-hex>
FRONTEND_URL=https://your-domain.com
RAILS_ENV=production
NODE_ENV=production
INSTALLATION_ENV=docker

# Database
POSTGRES_HOST=postgres
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=<password>
POSTGRES_DATABASE=chatwoot

# Redis
REDIS_URL=redis://redis:6379
REDIS_PASSWORD=<password>

# Rails
RAILS_LOG_TO_STDOUT=true
RAILS_SERVE_STATIC_FILES=true
RAILS_MAX_THREADS=5

Persistent Storage

Using Volumes

Mount volumes for persistent data:
services:
  rails:
    volumes:
      # User uploads
      - ./data/storage:/app/storage
      # Logs (optional)
      - ./data/logs:/app/log

Using S3 for Storage

For production, use S3-compatible storage:
# In .env
ACTIVE_STORAGE_SERVICE=amazon
S3_BUCKET_NAME=my-chatwoot-uploads
AWS_ACCESS_KEY_ID=<access-key>
AWS_SECRET_ACCESS_KEY=<secret-key>
AWS_REGION=us-east-1

Management Commands

View Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f rails
docker compose logs -f sidekiq

Access Rails Console

docker compose exec rails bundle exec rails console

Run Database Migrations

docker compose exec rails bundle exec rails db:migrate

Restart Services

# Restart all
docker compose restart

# Restart specific service
docker compose restart rails

Stop Services

docker compose down

Update Chatwoot

# Pull latest images
docker compose pull

# Restart with new images
docker compose up -d

# Run migrations
docker compose exec rails bundle exec rails db:migrate

Backup & Restore

Backup Database

# Create backup
docker compose exec postgres pg_dump -U postgres chatwoot > backup.sql

# Or use docker exec
docker exec chatwoot-postgres-1 pg_dump -U postgres chatwoot > backup.sql

Restore Database

# Restore from backup
docker compose exec -T postgres psql -U postgres chatwoot < backup.sql

Backup Volumes

# Backup storage volume
docker run --rm \
  -v chatwoot_postgres:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/postgres-backup.tar.gz /data

Resource Limits

Set resource limits for production:
services:
  rails:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 1G

  sidekiq:
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 2G
        reservations:
          cpus: '1'
          memory: 1G

Health Checks

Add health checks to your services:
services:
  rails:
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  postgres:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    healthcheck:
      test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

Troubleshooting

Container won’t start

Check logs:
docker compose logs rails

Database connection errors

Verify PostgreSQL is running:
docker compose ps postgres
docker compose exec postgres psql -U postgres -c "SELECT 1;"

Permission errors

Fix volume permissions:
sudo chown -R 1000:1000 ./data

Out of memory

Increase Docker memory limit in Docker Desktop settings or:
# Linux: Edit /etc/docker/daemon.json
{
  "default-runtime": "runc",
  "default-memory": "4g"
}

Next Steps

Build docs developers (and LLMs) love