What you will set up
Chatwoot is a shared inbox for customer support. Visitors write in a chat bubble on your website, customers email your support address, and your team answers both from one place in the browser, with assignment, teams, ready-made answers, labels and internal notes. It also has a help center for public articles, satisfaction surveys, reports, an API and webhooks. It does much of what Intercom's Messenger and inbox do, on your own server.
Chatwoot is made by Chatwoot Inc., a company whose terms of service follow Californian law, and developed in the open on GitHub. The core is open source under the MIT licence. Some features live in an enterprise folder under a separate licence, which needs a paid plan in production: step 18 has the list. This guide uses Chatwoot's own Community Edition image, chatwoot/chatwoot with a -ce tag, which leaves that folder out, so everything you run is MIT. Out of the box, Chatwoot checks for new versions with Chatwoot's server once a day, sends usage counts with it, and fetches a picture from Gravatar for every contact with an email address. Step 3 switches off the usage counts and Gravatar, and step 15 shows how to stop the version check too.
Here Chatwoot runs as a pod under a user of its own called chatwoot: the web app, a worker for background jobs, PostgreSQL with pgvector for the data, and Valkey for queues, behind Caddy from the Podman guide.
Every step below was run on a fresh Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13:
- Chatwoot 4.18.0 started with PostgreSQL 16 and Valkey 8. The first admin was created over an SSH tunnel before the site was public; after that, the setup page and sign-ups were closed.
- A website inbox was embedded in a plain HTML page. A headless browser opened the chat bubble and wrote; an agent's answer, and a ready-made answer typed with
/, appeared in the visitor's chat right away. An internal note stayed hidden from the visitor. - A second agent got an invitation by email, set a password, and got an email when a conversation was assigned to him. A team, labels and assignment worked, and Chatwoot saw each visitor's real address and ignored a forged
X-Forwarded-Forheader. - Outside business hours, a new chat got the automatic away message. After a conversation was resolved, the visitor rated it, and the rating showed under Reports > CSAT.
- An email inbox fetched mail over IMAP, sent the answer over SMTP with the right reply headers, and added the customer's reply to the same conversation. All mail went to a test mail server on the same machine, not to the internet.
- A help center article was published and readable without logging in. Through the API, a contact and a conversation were created, and a webhook received the events, signed.
- The interface and the chat bubble were shown in Swedish. A visitor's file was stored in the pod's volume. Deleting a contact removed their conversations and the file.
- With Gravatar switched off and the version check blocked as in step 15, a capture of the server's traffic showed neither.
- An update from Chatwoot 4.17.1 to 4.18.0 ran its database migrations by itself. Chatwoot was restored from a backup onto a fresh instance, with new database and Valkey passwords, and everything came back by itself after a reboot.
The pod used about 800 MB of memory: about 360 MB for the web app, 400 to 450 MB for the worker, and the rest for PostgreSQL and Valkey.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running;
- an A record and an AAAA record for
chat.example.compointing at your server; - an email account at your email provider that Chatwoot can send from, with its SMTP details.
The examples use chat.example.com for Chatwoot, www.example.com for your website, support@example.com for your support address, and 203.0.113.10 for your server's IPv4 address. Replace them throughout.
1. Create the user
As root:
useradd -m -s /bin/bash chatwoot
loginctl enable-linger chatwoot
machinectl shell chatwoot@
Everything up to step 5 runs as chatwoot.
2. Create the secrets
for s in chatwoot-secret-key-base chatwoot-db-password chatwoot-redis-password \
chatwoot-ar-primary-key chatwoot-ar-deterministic-key chatwoot-ar-salt; do
openssl rand -hex 32 | tr -d '\n' | podman secret create $s -
done
printf %s 'the-smtp-password' | podman secret create chatwoot-smtp-password -
The first loop makes six random values, stored by Podman and not in the files below:
chatwoot-secret-key-basesigns logins and the visitors' chat sessions.chatwoot-db-passwordandchatwoot-redis-passwordare the passwords for PostgreSQL and Valkey.- The three
chatwoot-ar-values are the keys Chatwoot encrypts two-factor secrets and webhook secrets with. Without them, two-factor login is not available.
Step 19 saves a copy of the key base and the three keys, as a restored backup is of little use without them. The last line stores the password of the email account Chatwoot sends from. Put your own between the quotes.
3. Define the pod
mkdir -p ~/.config/containers/systemd
cd ~/.config/containers/systemd
Create chatwoot.pod:
[Pod]
PodName=chatwoot
# Only Caddy, on this server, can reach Chatwoot: the port is not open to the internet.
PublishPort=127.0.0.1:8144:3000
# Give the pod addresses of its own, so Caddy's connections arrive from a private address that Chatwoot trusts.
Network=pasta:-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-a,fd00::2,-g,fd00::1
[Install]
WantedBy=default.target
Create chatwoot-db.container:
[Container]
ContainerName=chatwoot-db
Image=docker.io/pgvector/pgvector:pg16
Pod=chatwoot.pod
Volume=chatwoot-db:/var/lib/postgresql/data
Environment=POSTGRES_DB=chatwoot POSTGRES_USER=chatwoot
Secret=chatwoot-db-password,type=env,target=POSTGRES_PASSWORD
HealthCmd=pg_isready -U chatwoot -d chatwoot
HealthInterval=10s
Notify=healthy
AutoUpdate=registry
[Service]
Restart=always
Create chatwoot-redis.container:
[Container]
ContainerName=chatwoot-redis
Image=docker.io/valkey/valkey:8-alpine
Pod=chatwoot.pod
Volume=chatwoot-redis:/data
Secret=chatwoot-redis-password,type=env,target=REDIS_PASSWORD
Exec=sh -c 'exec valkey-server --requirepass "$REDIS_PASSWORD" --bind 127.0.0.1 --save 60 1'
AutoUpdate=registry
[Service]
Restart=always
Create chatwoot.env, the settings the web app and the worker share, with your own addresses and sender name:
FRONTEND_URL=https://chat.example.com
ENABLE_ACCOUNT_SIGNUP=false
DISABLE_TELEMETRY=true
DISABLE_GRAVATAR=true
TZ=Europe/Stockholm
RAILS_ENV=production
NODE_ENV=production
INSTALLATION_ENV=docker
RAILS_LOG_TO_STDOUT=true
POSTGRES_HOST=127.0.0.1
POSTGRES_USERNAME=chatwoot
POSTGRES_DATABASE=chatwoot
REDIS_URL=redis://127.0.0.1:6379
ACTIVE_STORAGE_SERVICE=local
MAILER_SENDER_EMAIL=Example AB support <support@example.com>
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_DOMAIN=example.com
SMTP_USERNAME=support@example.com
SMTP_AUTHENTICATION=login
SMTP_ENABLE_STARTTLS_AUTO=true
Create chatwoot-web.container:
[Unit]
Requires=chatwoot-db.service chatwoot-redis.service
After=chatwoot-db.service chatwoot-redis.service
[Container]
ContainerName=chatwoot-web
Image=docker.io/chatwoot/chatwoot:v4.18.0-ce
Pod=chatwoot.pod
Volume=chatwoot-storage:/app/storage
EnvironmentFile=chatwoot.env
Secret=chatwoot-secret-key-base,type=env,target=SECRET_KEY_BASE
Secret=chatwoot-db-password,type=env,target=POSTGRES_PASSWORD
Secret=chatwoot-redis-password,type=env,target=REDIS_PASSWORD
Secret=chatwoot-ar-primary-key,type=env,target=ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY
Secret=chatwoot-ar-deterministic-key,type=env,target=ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY
Secret=chatwoot-ar-salt,type=env,target=ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT
Secret=chatwoot-smtp-password,type=env,target=SMTP_PASSWORD
# Set up or migrate the database, then start the web server.
Exec=sh -c 'bundle exec rails db:chatwoot_prepare && exec bundle exec rails s -p 3000 -b 0.0.0.0'
[Service]
Restart=always
TimeoutStartSec=900
The worker uses the same image, settings and secrets, and runs Sidekiq instead. Make its file from the web app's:
sed -e 's/chatwoot-web/chatwoot-worker/' \
-e 's/^Requires=.*/Requires=chatwoot-web.service/' -e 's/^After=.*/After=chatwoot-web.service/' \
-e '/^# Set up/d' -e 's|^Exec=.*|Exec=bundle exec sidekiq -C config/sidekiq.yml|' \
chatwoot-web.container > chatwoot-worker.container
What the settings do:
Network=gives the pod its own private addresses. Caddy's connections then arrive from inside the pod's private network, which Chatwoot trusts to pass on the visitor's real address. That matters more than usual here: Chatwoot allows five logins per address in five minutes, and without this line every visitor would share the server's address, as the Podman guide explains.FRONTEND_URLis the address Chatwoot puts in emails, the chat bubble's code and the help center. It must be the publichttps://address.ENABLE_ACCOUNT_SIGNUP=falsekeeps the sign-up page closed. You add agents yourself (step 8).DISABLE_TELEMETRYandDISABLE_GRAVATARare explained in step 15.-cein the image tag is the Community Edition, andv4.18.0pins the version. Chatwoot is updated by changing the tag (step 20), as its database needs migrating, which the web app does each time it starts. PostgreSQL stays on 16 and Valkey on 8, andAutoUpdate=registryupdates them within those versions.- PostgreSQL comes from the pgvector project's image, as Chatwoot's database needs the
vectorextension. Valkey, an open-source fork of Redis, holds the background jobs and some short-lived state. - Files that visitors and agents send go in the
chatwoot-storagevolume. Step 14 has more.
Start it:
systemctl --user daemon-reload
systemctl --user start chatwoot-pod
systemctl --user enable --now podman-auto-update.timer
The first start downloads about 900 MB of images, and Chatwoot takes a minute or two to set up its database. It is ready when this prints a line with "version":"4.18.0":
curl -s http://127.0.0.1:8144/api; echo
4. Create the first admin
Until someone has created the first account, Chatwoot shows a setup page to anyone who opens it, and that account becomes the super admin of the whole installation. Create it before the site is public, through an SSH tunnel. From your own computer:
ssh -L 8144:127.0.0.1:8144 root@203.0.113.10
While that connection is open, open http://localhost:8144 in your browser. Fill in Name, Company Name, Work Email and Password. The password needs at least one special character, such as !. Untick Subscribe to release notes, newsletters & product feedback surveys., which would otherwise send your name, company and email address to Chatwoot. Choose Finish Setup, log in, and fill in the short form about your company. Website must be filled in before Continue works. You can close the tunnel afterwards.
http://localhost:8144/installation/onboarding now sends you back to the start page. Whoever opens it later gets the login page instead.
5. Put Caddy in front
Go back to root with exit, switch to machinectl shell caddy@, and add this block at the end of ~/Caddyfile:
chat.example.com {
reverse_proxy 127.0.0.1:8144
}
Restart Caddy with systemctl --user restart caddy, and open https://chat.example.com. Log in with the account from step 4. https://chat.example.com/app/auth/signup takes visitors to the login page, as sign-ups are off.
The super admin console, for installation-wide settings, is at https://chat.example.com/super_admin, with the same login.
6. Email
Chatwoot sends email for agent invitations, password resets, notifications to agents, such as when a conversation is assigned to them, and the answers in an email inbox (step 11, which uses its own settings). Step 3 points it at your email provider on port 587 with STARTTLS, with the password from step 2. For a provider that uses port 465 with TLS from the start, set SMTP_PORT=465, SMTP_ENABLE_STARTTLS_AUTO=false and add SMTP_SSL=true. We tested sending against a test mail server on the server itself, not against a real provider, and port 465 not at all.
Without working email, Chatwoot says nothing in the browser: invitations, password resets and notifications report success, and the worker's log shows the error. As chatwoot, podman logs chatwoot-worker shows Error performing ActionMailer::MailDeliveryJob. An agent you add then never gets their invitation. In the super admin console, under Users, you can open the agent, choose Edit, set a Password and a Confirmed At date, and give them the password yourself. We tested that.
To change email settings later, edit chatwoot.env as chatwoot, then:
systemctl --user daemon-reload
systemctl --user restart chatwoot-web chatwoot-worker
7. Live chat on your website
Under Settings > Inboxes, choose Add Inbox > Website. Fill in Website Name, such as your company's name, Website Domain, such as www.example.com, and a Welcome Heading and Welcome Tagline that the chat window shows. Choose Create inbox, pick the agents who will answer, and choose Add agents. As an administrator, add yourself too: only the agents of an inbox see its conversations.
The last page shows a script that starts like this:
<script>
(function(d,t) {
var BASE_URL="https://chat.example.com";
Paste it into every page of your website where the chat bubble should show, just before </body>. The bubble loads from your own Chatwoot. The script is also under the inbox's Configuration tab.
When a visitor writes, the conversation shows up under Conversations for the inbox's agents, and the answer appears in the visitor's chat window right away. The reply box sends with Ctrl + Enter. The visitor is first shown as a made-up name, such as long-sunset-735, until they give their email address in the chat. If they leave the page, their next visit continues the same conversation in the same browser.
The inbox's Settings tab has more: the bubble's colour and position, the reply time it promises, whether visitors can send files, and Enable email collect box, which asks visitors for their email address. Pre Chat Form asks for name and email before the chat starts.
8. Agents, teams and assignment
Under Settings > Agents, choose Add Agent, fill in Agent Name and Email Address, choose Agent or Administrator as Role, and Add Agent. The agent gets an email with an Accept invitation link where they set their password. Agents see and answer conversations in their inboxes; administrators can also change settings.
Under Settings > Teams, Create new team groups agents, such as Support and Sales. Conversations can be assigned to a team as well as to an agent.
To assign a conversation, open it, open the contact panel with the person icon at the top right, and set the agent and the team under Conversation Actions. The agent gets an email about it. Under Settings > Inboxes, the inbox's Collaborators tab has Enable automatic conversation assignment, which shares new conversations among the agents who are online in turn. Custom assignment policies need a paid plan.
9. Canned responses, labels and private notes
- Canned responses: under Settings > Canned Responses, Add canned response saves an answer with a Short code, such as
delivery. In a conversation, type/and part of the code, and pick the answer from the list. - Labels: under Settings > Labels, Add label creates labels such as
orderandrefund. Add them to a conversation under Conversation Actions. They show in the sidebar, where each one lists its conversations. - Private notes: Private Note above the reply box writes a note that only your team sees.
10. Business hours, auto-reply and CSAT
Under Settings > Inboxes, open the website inbox:
- Business Hours: turn on Enable business availability for this inbox, write the message visitors get outside your hours in Unavailable message for visitors, set Select timezone to Stockholm, which is Pacific time by default, set your days and hours, and save with Update business hours settings. Outside those hours, the chat window says when you are back, and a new conversation gets your message as an automatic answer.
- CSAT: turn on Enable CSAT and Update. When an agent resolves a conversation with Resolve, the visitor is asked to rate it from 😞 to 😍. The answers are under Reports > CSAT.
Reports also has an overview of open conversations and agents, and reports per conversation, agent, label, inbox and team. SLA under Reports needs a paid plan.
11. An email inbox
An email inbox fetches the mail sent to your support address and sends your answers from it, so that email and chat end up in one place. Chatwoot needs the account's IMAP and SMTP details, which your email provider lists.
- Under Settings > Inboxes, choose Add Inbox > Email > Other Providers. Fill in Channel Name, such as
Support email, and Email, your support address, and choose Create Email Channel. Add the agents, as in step 7. - Open the inbox's Configuration tab. Under IMAP, tick Enable IMAP configuration for this inbox, and fill in Address, such as
imap.example.com, Port993, Login and Password, with Enable SSL ticked. Choose Update IMAP settings. Chatwoot tries to log in first, and says "Please check the IMAP credentials and try again." if it fails. - Then, under SMTP, tick Enable SMTP configuration for this inbox, fill in your provider's SMTP server, port 587 and the same login, and choose Update SMTP settings.
Chatwoot checks the mailbox once a minute, and each new email starts a conversation, with the sender as a contact. Answers go out from the support address with the right reply headers, so the customer's next reply lands in the same conversation. We tested this against a test mail server on the same machine, with plain connections on its own ports; your provider's ports and SSL settings come from their documentation. For Gmail and Microsoft 365, the Google and Microsoft choices log in with OAuth instead, which needs an app registered with them first. We did not test those.
12. Help center
Help Center in the sidebar lets you publish articles for your customers, sorted into categories, in one or more languages. Create a portal, give it a name and a slug such as help, add Swedish under Locales, and write articles under Articles. Published articles are public, at https://chat.example.com/hc/help/sv. The help center loaded no outside resources in our test.
Under the website inbox's Settings tab, Help Center links the portal to the chat window, so visitors can read articles there.
13. API and webhooks
Everything in the dashboard can be done through Chatwoot's API. Your personal Access Token is under your profile, at the bottom left, > Profile Settings. The account number is under Settings > Account Settings > Account ID. With those, from any computer, this creates a contact in the email inbox, with the inbox's number from the address bar on its settings page:
curl -s -X POST https://chat.example.com/api/v1/accounts/1/contacts \
-H 'api-access-token: YOUR_TOKEN' -H 'Content-Type: application/json' \
-d '{"inbox_id": 2, "name": "Erik Exempel", "email": "erik@example.net"}'
The answer has the contact's id, and source_id under contact_inbox. This starts a conversation with them, which in an email inbox sends the message as an email:
curl -s -X POST https://chat.example.com/api/v1/accounts/1/conversations \
-H 'api-access-token: YOUR_TOKEN' -H 'Content-Type: application/json' \
-d '{"inbox_id": 2, "contact_id": 5, "source_id": "erik@example.net", "message": {"content": "Hej Erik, din reservdel har kommit in."}}'
Chatwoot's documentation writes the header as api_access_token, with underscores. Caddy has dropped headers with underscores in their names since version 2.11.4, so through Caddy, use api-access-token, which Chatwoot reads the same way. For a tool that only sends the underscore version, such as an integration you cannot change, Caddy 2.11.6 and later can keep it. Add this to the global block at the top of ~/Caddyfile, as caddy, and restart Caddy:
{
admin off
servers {
expected_underscore_headers api_access_token
}
}
Caddy calls the option experimental. We tested both ways.
Webhooks send events to another system as they happen. Under Settings > Integrations > Webhooks, Add new webhook takes a URL and the events to send, such as Conversation Created and Message Created. Each request has an X-Chatwoot-Signature header: sha256= and an HMAC-SHA256 of the X-Chatwoot-Timestamp header, a dot and the body, made with the webhook's secret. Check it before you trust the request. Chatwoot only calls public addresses, not addresses in private networks or on the server itself. The Network= line from step 3 is the second fix in step 8 of the Podman guide, which lets a webhook reach another app on this same server, such as n8n, by its public name. We did not test such a webhook.
14. Files and attachments
Files that visitors and agents send, avatars and help center images are stored in the chatwoot-storage volume, since ACTIVE_STORAGE_SERVICE=local. Chatwoot gives each file a long, random address, and anyone who has that address can download the file without logging in, also after the conversation is over. Keep that in mind before you ask customers to send ID documents.
To store files in S3 instead, such as on Garage, Chatwoot has ACTIVE_STORAGE_SERVICE=s3_compatible and the related settings in its documentation. We did not test that.
15. Outside services
With the settings from step 3, Chatwoot made two kinds of outside connections in our capture of the server's DNS lookups and outgoing connections:
- The version check: once a day, the worker sends Chatwoot's server,
hub.2.chatwoot.com, a random installation ID, the version, the installation's host name and its edition, and gets the latest version back, which the super admin console shows. WithoutDISABLE_TELEMETRY=true, it also sends the number of accounts, users, inboxes, conversations and messages. There is no setting that stops the check itself. To stop it, add this line under[Pod]inchatwoot.podand restart the pod. The worker's log then shows a refused connection once a day:AddHost=hub.2.chatwoot.com:127.0.0.1 - Gravatar: without
DISABLE_GRAVATAR=true, Chatwoot looks up every contact and agent with an email address atwww.gravatar.com, run by Automattic in the United States, by sending an MD5 hash of the address. In our capture, contacts from the email inbox caused lookups ofwww.gravatar.com. With the line, a new contact with an email address caused none.
The rest follows what you set up: your email provider, the webhook addresses you add, and the image registries when Podman updates. The chat bubble on your website loads from your own server only. Chatwoot's mobile apps get their push notifications through Chatwoot's server, which is what ENABLE_PUSH_RELAY_SERVER controls. We did not test the apps. The setup page in step 4 registers your installation with Chatwoot only if the newsletter box stays ticked.
16. Use Chatwoot in Swedish
Chatwoot comes with a Swedish translation, which is incomplete: some menus and messages stay in English. Under Settings > Account Settings, set Site language to Svenska and choose Update settings. That switches the dashboard for everyone who has not picked a language, and the chat bubble on your website, which then says Starta konversation and Vi är borta för tillfället. Each agent can choose their own language under Profile Settings > Preferred Language.
Texts you write yourself, such as the welcome heading, the away message and canned responses, are shown as you wrote them, so write them in Swedish.
17. Personal data
Chatwoot stores the people who write to you as contacts, with their conversations, files and ratings. To delete someone, for example when they ask under GDPR, open Contacts, open the person, and choose Ta bort kontakt, or Delete contact in English, then confirm. Visitors who never gave their email address are not listed under Contacts: open their conversation, open the contact panel with the person icon, and the link icon next to their name. Deleting a contact deletes their conversations, messages and files too. In our test, the conversation and the file in the volume were gone afterwards.
18. Free and paid features
Chatwoot's pricing page for self-hosting lists three editions, as of 14 October 2026: the free Community Edition, Premium Support at $19 per agent and month, and Enterprise Edition at $99 per agent and month. Everything in this guide works in the Community Edition. The paid editions add, according to that page:
- Premium: Captain, Chatwoot's AI assistant, voice calls, custom branding, such as removing Powered by Chatwoot from the chat bubble, agent capacity, custom roles and permissions, and priority support.
- Enterprise, also: single sign-on with SAML, and SLA policies.
The super admin console lists audit logs as paid as well. The -ce image does not contain this code; the dashboard still shows some of these items, with a prompt to upgrade.
For comparison, Intercom's pricing page lists its plans at $29, $85 and $132 per seat and month, plus from $0.99 per outcome for its AI agent Fin, as of 14 October 2026. Its help center says data hosting in the EU is only available to new workspaces on the Advanced and Expert plans, on a contract.
19. Back up
As chatwoot:
umask 077
mkdir -p ~/backup
podman exec chatwoot-db pg_dump -U chatwoot chatwoot > ~/backup/chatwoot-db.sql
podman volume export chatwoot-storage > ~/backup/chatwoot-storage.tar
for s in chatwoot-secret-key-base chatwoot-ar-primary-key chatwoot-ar-deterministic-key chatwoot-ar-salt; do
podman secret inspect --showsecret --format '{{.SecretData}}' $s > ~/backup/$s.txt
done
cp ~/.config/containers/systemd/chatwoot.env ~/backup/
The database holds the conversations, contacts, users, settings and help center; the volume holds the files; and the four keys keep logins, two-factor login and webhook secrets working. The dump runs while Chatwoot keeps working. Valkey only holds short-lived data and needs no backup. Copy ~/backup to another machine, for example with restic, and store it safely: it holds all your customers' conversations.
To restore onto a fresh instance, set it up as in steps 1 to 3, but in step 2 create the four keys from the backup instead of new ones:
cd ~/backup
for s in chatwoot-secret-key-base chatwoot-ar-primary-key chatwoot-ar-deterministic-key chatwoot-ar-salt; do
tr -d '\n' < $s.txt | podman secret create $s -
done
Create the other secrets as in step 2. Before the first start, load the files, then start the pod and wait until it answers, as in step 3:
podman volume create chatwoot-storage
podman volume import chatwoot-storage ~/backup/chatwoot-storage.tar
systemctl --user daemon-reload
systemctl --user start chatwoot-pod
Chatwoot has now set up an empty database, and opened the setup page from step 4. Replace the database with the backup, and empty Valkey, which closes the setup page again:
systemctl --user stop chatwoot-web
podman exec chatwoot-db dropdb -U chatwoot chatwoot
podman exec chatwoot-db createdb -U chatwoot chatwoot
podman exec -i chatwoot-db psql -q -U chatwoot -d chatwoot < ~/backup/chatwoot-db.sql
podman exec chatwoot-redis sh -c 'valkey-cli -a "$REDIS_PASSWORD" --no-auth-warning FLUSHALL'
systemctl --user start chatwoot-web chatwoot-worker
Do this before you add the site to Caddy, or the setup page is open to anyone for that while. Stopping the web app stops the worker too. Afterwards, curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8144/installation/onboarding should print 302.
20. Update
Read the release notes on Chatwoot's GitHub page first. Then, as chatwoot, change the version in both files, here to a made-up 4.19.0, and restart:
cd ~/.config/containers/systemd
sed -i 's/chatwoot:v4.18.0-ce/chatwoot:v4.19.0-ce/' chatwoot-web.container chatwoot-worker.container
systemctl --user daemon-reload
systemctl --user restart chatwoot-web chatwoot-worker
The web app migrates the database when it starts; podman logs chatwoot-web shows each migration. Our update from 4.17.1 to 4.18.0 took about a minute and ran three migrations. Take a backup first. When it works, podman image prune -a removes the old image, which takes about 1.8 GB.
podman auto-update keeps PostgreSQL 16 and Valkey 8 up to date by itself. Moving PostgreSQL to a new major version needs a dump and restore, as in step 19.
Troubleshooting
"Retry later" when you log in. Chatwoot allows five logins per address in five minutes, also successful ones. Wait five minutes. If it happens to many people at once, the Network= line from step 3 is missing, so everyone shares the server's address: podman logs chatwoot-web then shows 203.0.113.10 after Started POST "/auth/sign_in" for.
"You need to sign in or sign up before continuing." from the API, with the right token. The header has underscores and Caddy dropped it. Use api-access-token, or keep the header as in step 13.
Invitations and password resets never arrive. Check podman logs chatwoot-worker for MailDeliveryJob errors. Net::OpenTimeout means the SMTP server cannot be reached, and Broken pipe means SMTP_ADDRESS is empty.
"Please check the IMAP credentials and try again." podman logs chatwoot-web shows why after check_imap_connection failed with. "Unsupported authentication mechanism 'PLAIN'" means the server wants another method: choose login under Authentication.
The setup page from step 4 shows after a restore. Valkey was not emptied. Run the FLUSHALL line from step 19.