The Problem

Businesses that need a unified inbox for live‑chat, email, and social‑media messaging often rely on SaaS tools that lock them into third‑party data stores and expensive licences. When data‑privacy, customisation, or cost become blockers, an on‑premise alternative is required.

What This Does

chatwoot is a Ruby on Rails application that implements a full‑stack omnichannel support desk. The core request handling lives under app/controllers/api/v1/ (e.g., app/controllers/api/v1/accounts/conversationscontroller.rb). Conversation‑building logic is encapsulated in the app/builders/ hierarchy, such as app/builders/conversationbuilder.rb and the various app/builders/v2/reports/* classes that generate analytics. Front‑end assets (SCSS, JS) are compiled from app/assets/, while background jobs and websockets are defined in app/channels/ (roomchannel.rb). Deployment‑ready definitions are provided via Procfile, Dockerfile (in .devcontainer/Dockerfile), and a Docker‑Compose file (.devcontainer/docker-compose.yml). Dependency management is handled by Bundler (Gemfile + Gemfile.lock), and the Ruby version is pinned in .ruby-version.

How To Use It

Setup

Install Ruby version defined in .ruby-version (e.g., rbenv or rvm) rbenv install $(cat .ruby-version) rbenv local $(cat .ruby-version)

Install gems

bundle install

Copy example env and edit required keys

cp .env.example .env Edit .env – at minimum DATABASEURL, REDISURL, and SECRETKEYBASE

Containerised option (recommended for quick start)

docker compose -f .devcontainer/docker-compose.yml up -d The compose file builds the image from .devcontainer/Dockerfile.base and starts Rails (web) and Redis/Postgres services.

Running locally

Database preparation bundle exec rake db:setup # reads config/database.yml Start the Rails server bundle exec rails server -p 3000

Entry points

Procfile – used by Heroku/Docker to launch bundle exec rails server. Rakefile – provides tasks such as rake db:migrate and rake spec.

Testing

bundle exec rspec spec # 5 test files are present under spec/

Real‑World Use

A SaaS product can embed the widget by loading the script served from /widget.js (generated from app/assets/javascripts/secretField.js). Incoming messages are posted to the API endpoint POST /api/v1/accounts/:accountid/conversations/:conversationid/messages handled by app/controllers/api/v1/accounts/conversations/messagescontroller.rb. The response is broadcast over the ActionCable RoomChannel, enabling real‑time updates in the web UI.

Example: creating a message via the API client

client.post "/api/v1/accounts/1/conversations/42/messages", { content: "Hello, support!" }.tojson, { "Authorization" => "Bearer <apitoken>" }

Code Health & Issues

Low – Test coverage limited – only 5 spec files for a 200‑file codebase; many controllers and builders lack direct tests (app/controllers/api/v1/...). Medium – Environment‑variable exposure – .env.example is present but the required keys are not documented in README; missing explicit validation in app/controllers/api/basecontroller.rb. Low – Dockerfile location – production Dockerfile resides only under .devcontainer/; CI builds (.github/workflows/publishfoss_docker.yml) may rely on it, but a top‑level Dockerfile would be clearer. Low – Dependency hygiene – Gemfile.lock is committed, and GitHub Dependabot is configured (.dependabot/config.yml), indicating active security monitoring. None – Licensing – LICENSE file present (MIT). None – CI/CD – Multiple GitHub Actions workflows run linting, tests, and Docker builds, showing a mature pipeline.

The Bottom Line

Chatwoot delivers a self‑hosted, Rails‑based omnichannel support platform with a clear modular structure and ready‑to‑use Docker configuration. It is well‑suited for teams that need full data control and are comfortable managing a Ruby stack. The main limitation is sparse automated test coverage, so organisations should allocate effort for additional testing before large‑scale production use.