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.