Skip to main content

How to Self-Host Chatwoot in 2026 (After the Cloud API Paywall)

· 12 min read

Self-hosted Chatwoot is free under the MIT licence and keeps full API and webhook access. On Chatwoot Cloud, the free plan lost both on 16 July 2026. To self-host it you need four things: the Chatwoot Community Edition image (chatwoot/chatwoot:<version>-ce), PostgreSQL with the pgvector extension, Redis, and a Sidekiq worker next to the Rails server. The Docker Compose file below starts all four. It idles at about 810 MB of RAM.

Conflict of interest, stated up front: I run Hostim.dev, a hosting platform, and there is a one-click Chatwoot template on it. A pricing change that pushes people toward self-hosting is good for me. So every number below is either measured or linked, and the Docker Compose path runs on any server you like, not on ours.


What changed on Chatwoot Cloud​

On 16 July 2026 Chatwoot announced that API and webhook access on Chatwoot Cloud now needs a paid plan. Existing free accounts got a two-week transition. New free accounts get no API access at all. Their reason is abuse: free accounts used the API and webhooks for spam at scale, and that cost them infrastructure and moderation money.

That is a fair reason. Chatwoot built a good product, gave it away under MIT, and is allowed to charge for running it. The same post says it plainly: "developers who self-host Chatwoot can continue to use APIs and webhooks as part of their own deployment."

What the change looks like from the inside is less clear. Our token still passed GET /api/v1/profile with a 200 and the role administrator. Every call scoped to the account returned a 403 with "API access is not enabled for this account". It looks like a broken token, not a paywall. If you see that error on a free Cloud account, your token is fine. The plan is the reason.

Chatwoot Cloud pricing vs self-hosted​

Prices from the Chatwoot pricing page, checked on 30 September 2026:

OptionPriceAPI and webhooksLimits
Cloud Hacker (free)$0No, since July 20262 agents, 500 conversations/month, 30 days data retention
Cloud Startups$19 per agent/monthYes–
Cloud Business$39 per agent/monthYes–
Cloud Enterprise$99 per agent/monthYes–
Self-hosted Community EditionYour serverYesNo enterprise features (see below)

With two agents, Startups costs $38 a month and Business costs $78. Self-hosted cost does not grow with the number of agents. It is only the server.

Our own instance is the one that runs the support chat on hostim.dev. It costs €7.50 a month on Hostim: an app with 2 vCPU and 2 GB RAM (€4.50), a 1 GB shared Postgres (€1), a 0.5 GB Redis (€1) and a 5 GB volume for attachments (€1). That is our price list, so read it with the conflict above in mind. On your own VPS, what matters is RAM. The Docker Compose stack below used this much memory right after first boot:

ContainerRAM at idle
rails373 MB
sidekiq381 MB
postgres (pgvector)44 MB
redis12 MB

That is about 810 MB before any real traffic, so plan for at least 2 GB of RAM.

Is Chatwoot free? The licence in one paragraph​

Mostly yes. The repository licence is MIT for everything except the enterprise/ directory. That directory is under the Chatwoot Enterprise licence: you may read it and test it, but using it in production needs a paid subscription. Chatwoot publishes two image flavours, and the difference matters:

Image tagContainsProduction use without a licence
chatwoot/chatwoot:v4.18.0-ce / latest-ceMIT code onlyYes
chatwoot/chatwoot:v4.18.0 / latestMIT code plus enterprise/Only the MIT parts; enterprise features need a licence

Use the -ce tag. You lose custom dashboards, SLA management, audit logs and the Captain AI assistant. Live chat, email, the API, webhooks, automations, macros and canned responses all stay. The official docker-compose.production.yaml uses latest, the image with the enterprise code. That is the one line I change first.

Self-host Chatwoot with Docker Compose​

Tested on 30 September 2026 with Chatwoot 4.18.0 and Docker Compose v5.5.

1. The compose file​

This is the official production file with four changes: the CE image pinned to a version, restart: unless-stopped, Postgres and Redis not published on the host, and Redis getting its password from the environment.

docker-compose.yml
x-chatwoot: &chatwoot
image: chatwoot/chatwoot:v4.18.0-ce
env_file: .env
volumes:
- storage:/app/storage
depends_on:
- postgres
- redis
restart: unless-stopped

services:
rails:
<<: *chatwoot
entrypoint: docker/entrypoints/rails.sh
command: ["bundle", "exec", "rails", "s", "-p", "3000", "-b", "0.0.0.0"]
ports:
- "127.0.0.1:3000:3000"

sidekiq:
<<: *chatwoot
command: ["bundle", "exec", "sidekiq", "-C", "config/sidekiq.yml"]

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

redis:
image: redis:7-alpine
restart: unless-stopped
command: ["sh", "-c", "redis-server --requirepass \"$$REDIS_PASSWORD\""]
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- redis:/data

volumes:
storage:
postgres:
redis:

Two services run the same image. rails serves the dashboard, the widget and the API. sidekiq sends emails, delivers webhooks and runs automations. If you forget sidekiq, the dashboard works and nothing is ever sent, with no error.

Postgres must be the pgvector image. Chatwoot 4.x migrations create the vector and pg_trgm extensions, and the plain postgres:16 image does not ship vector.

2. The .env file​

cat > .env <<EOF
RAILS_ENV=production
NODE_ENV=production
INSTALLATION_ENV=docker
SECRET_KEY_BASE=$(openssl rand -hex 64)
FRONTEND_URL=https://chat.example.com
FORCE_SSL=false
ENABLE_ACCOUNT_SIGNUP=false
POSTGRES_HOST=postgres
POSTGRES_USERNAME=postgres
POSTGRES_PASSWORD=$(openssl rand -hex 16)
POSTGRES_DATABASE=chatwoot
REDIS_URL=redis://redis:6379
REDIS_PASSWORD=$(openssl rand -hex 16)
ACTIVE_STORAGE_SERVICE=local
EOF

SECRET_KEY_BASE is generated once. Do not change it later: sessions and encrypted fields depend on it. FRONTEND_URL must be the public HTTPS address. The section on traps below explains why. For email, add SMTP_ADDRESS, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD and MAILER_SENDER_EMAIL. Without them, agent invites and password resets fail with no error.

3. Prepare the database and start​

docker compose run --rm rails bundle exec rails db:chatwoot_prepare
docker compose up -d
curl -s localhost:3000/api
# {"version":"4.18.0","timestamp":"…","queue_services":"ok","data_services":"ok"}

The first command prints ERROR -- : Failed to configure AI Agents SDK: PG::UndefinedTable. That comes from an initializer that runs before the tables exist. It is harmless. When it finishes you have 180 migrations and the vector and pg_trgm extensions.

4. Create the first admin​

Sign-up is off, so create the admin from the Rails console:

docker compose exec rails bundle exec rails runner "
u = User.new(name: 'Admin', email: 'you@example.com', password: 'a-long-password', confirmed_at: Time.now); u.save!
a = Account.create!(name: 'My Company')
AccountUser.create!(account: a, user: u, role: :administrator)"

Or set ENABLE_ACCOUNT_SIGNUP=true, register in the browser, then set it back to false and run docker compose up -d again.

5. HTTPS in front​

Port 3000 listens on 127.0.0.1 only. Put a reverse proxy in front for TLS. With Caddy it is two lines:

Caddyfile
chat.example.com {
reverse_proxy 127.0.0.1:3000
}

Caddy passes WebSocket upgrades by default, and the live dashboard needs them. If you are choosing between Caddy, Traefik, Nginx and HAProxy, I compared them in Reverse Proxy Showdown.

6. Check that the API works​

In the dashboard, open your profile settings and copy the access token. Then:

curl -H "api_access_token: $TOKEN" https://chat.example.com/api/v1/accounts/1/conversations

A 200 with {"data":{"meta":…}} means you have the thing the Cloud free plan no longer gives you.

Three traps we hit moving our own support desk​

We moved hostim.dev's support chat off Chatwoot Cloud on 30 August 2026. The install was the easy part. These three were not.

FORCE_SSL=true behind a proxy or Kubernetes ingress keeps the app from ever becoming healthy. Rails answers the health check with a 301 to https://. The health checker follows it, speaks TLS to plain-HTTP Puma, fails, and the app never counts as ready. A customer's Chatwoot on our platform sat unready for 41 hours this way, with the old version still serving, so nobody noticed. If TLS ends at a proxy, leave FORCE_SSL=false. The proxy already redirects HTTP to HTTPS. This applies to any Rails app, not only Chatwoot.

FRONTEND_URL is not read from the request. Chatwoot builds the widget script URL, the links in emails and the OAuth callbacks from this one variable. Add a custom domain without changing it, and the widget on your site keeps loading from the old host. Change it, then restart both rails and sidekiq.

Identity validation breaks during a cutover. If you use HMAC identity validation on the website inbox, the identifier_hash comes from your backend and the key lives in Chatwoot. You cannot switch both at the same moment, and in either order, logged-in users fail validation for a while. The boring fix works: turn identity validation off on the inbox, switch the backend and the widget, turn it back on.

Getting your conversations out of Chatwoot Cloud​

This was the hardest part, and it matters whether you self-host or not: Chatwoot has no conversation export. Not on Cloud, not self-hosted. The feature request (#5231, opened in 2022) was closed in November 2025 for inactivity, not because it shipped. The migration docs only cover importing into Chatwoot from Intercom, Front, Freshdesk and Zendesk.

What we tried on our free Cloud account in August 2026:

MethodResult
API with an access token403, API is paid now
"Send email transcript" macro or automation"Not available on your current plan"
Contacts CSV exportWorks, but only names and emails, no conversations
The dashboard's own requests, replayed for our own accountWorked

The last one is the dashboard doing what it always does. It loads your conversations from the same API, signed in as you. We paged through our own account that way and saved 56 conversations, 478 messages and 4 attachments as JSON and Markdown. Download attachments straight away: their URLs stop working when the account closes.

To be clear, this is getting your own data out of your own account. It does not unlock anything paid. The narrow complaint I have is not about pricing. It is that a support tool keeps the one thing you cannot export, the conversations, and that is a data portability problem on any plan.

If you self-host, you do not have this problem: the conversations are rows in your own Postgres, and pg_dump is the export.

The one-click version​

If you do not want to run the compose file, the Hostim template deploys the same stack: the CE image, Postgres with pg_trgm and vector already enabled, Redis, and a volume for attachments. It runs Rails and Sidekiq in one container to save one app's cost, and it already avoids FORCE_SSL. It is the setup that runs our own support chat.

Deploy Chatwoot on Hostim

FAQ​

Frequently asked questions

Is Chatwoot free to self-host?

Yes. Everything outside the enterprise/ directory is MIT licensed, and the Community Edition image (tags ending in -ce) contains only that code. You can run it in production for any number of agents without paying Chatwoot. Custom dashboards, SLA management, audit logs and Captain AI need a paid Enterprise licence.

Does the Chatwoot free plan include API access?

Not any more. Since 16 July 2026, API and webhook access on Chatwoot Cloud needs a paid plan (Startups at $19 per agent per month or higher). Self-hosted Chatwoot keeps full API and webhook access on every edition.

Why does the Chatwoot API return 403 'API access is not enabled for this account'?

Your Chatwoot Cloud account is on the free plan. The token is valid, and profile calls still succeed, but every account-scoped endpoint is blocked since the July 2026 change. Upgrade the plan or self-host.

How much RAM does self-hosted Chatwoot need?

Our Docker Compose stack of Chatwoot 4.18.0 used about 810 MB at idle: 373 MB for Rails, 381 MB for Sidekiq, 44 MB for Postgres and 12 MB for Redis. Plan for at least 2 GB of RAM on the server.

Can I export conversations from Chatwoot?

There is no built-in conversation export on Cloud or self-hosted. Contacts can be exported as CSV. On a self-hosted instance the conversations are in your own PostgreSQL database, so pg_dump works. On Cloud, paid plans can read them through the API.

Which Postgres image does Chatwoot need?

One with the pgvector extension, for example pgvector/pgvector:pg16. Chatwoot 4.x migrations create the vector and pg_trgm extensions, and the plain postgres image does not include vector.


Last updated: 30 September 2026. Tested with Chatwoot 4.18.0 (CE), pgvector/pgvector:pg16, redis:7-alpine and Docker Compose v5.5. Chatwoot Cloud prices were checked on the same day. They change, so check the pricing page before you decide.