Zammad в Docker Compose: готовый файл
Zammad — не «просто ещё один helpdesk», а полноценная связка из веб-приложения на Rails, очередей, полнотекстового поиска и вебсокетов для realtime-обновлений тикетов. Развернуть его одним docker run не получится: нужен Postgres, Redis, Elasticsearch и минимум три процесса самого Zammad, которые должны стартовать в правильном порядке. Ниже — рабочий compose-файл, который поднимает весь стек сразу, и разбор, что в нём происходит и на чём чаще всего спотыкаются при первом запуске.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Архитектура: из чего состоит Zammad
Прежде чем копировать конфиг, полезно понимать, что именно вы поднимаете — это экономит часы отладки, когда что-то не стартует.
- PostgreSQL — основная база: тикеты, пользователи, история, настройки.
- Elasticsearch — полнотекстовый поиск по тикетам и вложениям. Без него Zammad стартует, но поиск и часть виджетов в интерфейсе не работают, а логи засыпаны предупреждениями.
- Redis — очереди фоновых задач и кеш сессий.
- Memcached — дополнительный кеш для Rails.
- zammad-railsserver — сам веб-бэкенд (Puma), отдаёт API.
- zammad-scheduler — крутит периодические задачи: проверку почтовых ящиков, эскалации SLA, напоминания.
- zammad-websocket — держит соединения для live-обновлений в интерфейсе (когда коллега ответил на тикет, вы видите это без перезагрузки страницы).
- zammad-nginx — отдаёт статику и проксирует запросы к railsserver и websocket на один порт.
- zammad-init — служебный контейнер, который при первом запуске накатывает миграции базы и настраивает Elasticsearch-индексы, после чего завершает работу.
Все процессы Zammad, кроме init, используют один и тот же образ приложения — просто с разными командами запуска. Это удобно: обновление версии сводится к смене одного тега во всех сервисах разом.
Готовый docker-compose.yml
Файл ниже рассчитан на прод-сценарий: данные лежат в именованных volume, порт наружу не торчит напрямую (в конце статьи — вариант с Traefik). Версии образов лучше свериться на Docker Hub перед разворачиванием — Zammad обновляется каждые несколько недель, и фиксировать в проде «latest» не стоит.
# docker-compose.yml
services:
zammad-postgresql:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRESQL_DB:-zammad_production}
POSTGRES_USER: ${POSTGRESQL_USER:-zammad}
POSTGRES_PASSWORD: ${POSTGRESQL_PASS}
volumes:
- zammad-postgresql-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRESQL_USER:-zammad}"]
interval: 10s
timeout: 5s
retries: 10
zammad-redis:
image: redis:7-alpine
restart: unless-stopped
command: ["redis-server", "--save", "60", "1"]
volumes:
- zammad-redis-data:/data
zammad-memcached:
image: memcached:1.6-alpine
restart: unless-stopped
command: ["-m", "256"]
zammad-elasticsearch:
image: zammad/zammad-docker-compose:zammad-elasticsearch-${ZAMMAD_VERSION:-6.5}
restart: unless-stopped
environment:
discovery.type: single-node
xpack.security.enabled: "false"
ES_JAVA_OPTS: "-Xms512m -Xmx512m"
volumes:
- zammad-elasticsearch-data:/usr/share/elasticsearch/data
zammad-init:
image: zammad/zammad-docker-compose:zammad-${ZAMMAD_VERSION:-6.5}
command: ["zammad-init"]
restart: "no"
depends_on:
- zammad-postgresql
- zammad-redis
- zammad-memcached
- zammad-elasticsearch
env_file: .env
volumes:
- zammad-storage:/opt/zammad/storage
zammad-railsserver:
image: zammad/zammad-docker-compose:zammad-${ZAMMAD_VERSION:-6.5}
command: ["zammad-railsserver"]
restart: unless-stopped
depends_on:
- zammad-init
env_file: .env
volumes:
- zammad-storage:/opt/zammad/storage
zammad-scheduler:
image: zammad/zammad-docker-compose:zammad-${ZAMMAD_VERSION:-6.5}
command: ["zammad-scheduler"]
restart: unless-stopped
depends_on:
- zammad-init
env_file: .env
volumes:
- zammad-storage:/opt/zammad/storage
zammad-websocket:
image: zammad/zammad-docker-compose:zammad-${ZAMMAD_VERSION:-6.5}
command: ["zammad-websocket"]
restart: unless-stopped
depends_on:
- zammad-init
env_file: .env
zammad-nginx:
image: zammad/zammad-docker-compose:zammad-${ZAMMAD_VERSION:-6.5}
command: ["zammad-nginx"]
restart: unless-stopped
depends_on:
- zammad-railsserver
- zammad-websocket
env_file: .env
ports:
- "127.0.0.1:8080:8080"
volumes:
- zammad-storage:/opt/zammad/storage
volumes:
zammad-postgresql-data:
zammad-redis-data:
zammad-elasticsearch-data:
zammad-storage:
Порт вынесен на 127.0.0.1, потому что снаружи сервис отдаёт HTTPS-прокси (Traefik или Caddy) — вариант конфига разберём ниже.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПеременные окружения в .env
Отдельный файл .env рядом с compose держит секреты и параметры подключения к внешним сервисам:
# .env
ZAMMAD_VERSION=6.5
POSTGRESQL_DB=zammad_production
POSTGRESQL_USER=zammad
POSTGRESQL_PASS=замените-на-длинный-случайный-пароль
POSTGRESQL_HOST=zammad-postgresql
POSTGRESQL_PORT=5432
REDIS_URL=redis://zammad-redis:6379
MEMCACHE_SERVERS=zammad-memcached:11211
ELASTICSEARCH_HOST=zammad-elasticsearch
ELASTICSEARCH_PORT=9200
ELASTICSEARCH_SCHEMA=http
NGINX_SERVER_NAME=helpdesk.example.com
NGINX_PORT=8080
RAILS_TRUSTED_PROXIES=172.16.0.0/12,10.0.0.0/8
Пароль от Postgres генерируйте так, чтобы не пришлось вспоминать его вручную:
openssl rand -base64 32
RAILS_TRUSTED_PROXIES важен, если Zammad стоит за реверс-прокси: без него в логах будет виден IP прокси вместо реального адреса клиента, а в некоторых версиях — редиректы будут уводить на http вместо https.
Первый запуск и инициализация
Порядок действий:
mkdir -p /opt/zammad && cd /opt/zammad
# положите сюда docker-compose.yml и .env
docker compose up -d zammad-postgresql zammad-redis zammad-memcached zammad-elasticsearch
# дайте базам подняться, особенно Elasticsearch — на слабом сервере это может занять минуту-две
docker compose ps
docker compose up zammad-init
# дождитесь, пока контейнер завершится с кодом 0 — это значит, миграции и индексы накатились
docker compose up -d
docker compose logs -f zammad-railsserver
Первый вход выполняется через мастер настройки в браузере: откройте http://ваш-сервер:8080 (или домен, если уже настроили прокси), задайте админ-аккаунт, часовой пояс и базовые каналы. На слабом сервере инициализация Elasticsearch и первый прогрев заметно нагружают CPU на пару минут — это нормально, не повод перезапускать контейнеры.
Если zammad-init падает с ошибкой подключения к базе — почти всегда дело в том, что Postgres ещё не готов принимать соединения. Перезапустите docker compose up zammad-init ещё раз через 15–20 секунд.
HTTPS снаружи через Traefik или Caddy
Отдавать Zammad напрямую по HTTP не стоит — в интерфейсе передаются пароли и токены. Проще всего добавить перед ним обратный прокси с автоматическим SSL. Если Traefik уже развёрнут на сервере, добавьте лейблы к сервису zammad-nginx вместо публикации порта:
zammad-nginx:
# ...
ports: []
networks:
- traefik-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.zammad.rule=Host(`helpdesk.example.com`)"
- "traefik.http.routers.zammad.entrypoints=websecure"
- "traefik.http.routers.zammad.tls.certresolver=letsencrypt"
- "traefik.http.services.zammad.loadbalancer.server.port=8080"
networks:
traefik-public:
external: true
Подробно про настройку самого Traefik как реверс-прокси для докер-сервисов — в отдельной статье: Traefik как reverse proxy для Docker. Если вместо Traefik используете Caddy, там будет одна строка helpdesk.example.com { reverse_proxy zammad-nginx:8080 } — конфиг сильно проще, но без динамического обнаружения контейнеров.
Подключение почты, чата и телефонии
Ключевая ценность Zammad как service desk — не сам тикет-интерфейс, а сведение разных каналов в одну очередь.
- Почта. В админке (
Channels → Email) добавляется IMAP-ящик — Zammad сам опрашивает его черезzammad-schedulerи создаёт тикеты из новых писем. Для исходящей почты нужен SMTP-релей; если поднимаете свой почтовый сервер на том же хостинге, конфигурация Postfix описана в статье Postfix на VPS. Часто проще и надёжнее использовать внешний SMTP-провайдер — так письма реже улетают в спам у получателей. - Чат на сайте. Виджет чата встраивается одной строкой
<script>из разделаChannels → Chat— работает через тот жеzammad-websocket, отдельных портов открывать не нужно. - Телефония. Zammad интегрируется с провайдерами через CTI-адаптеры (например, sipgate или собственный SIP-шлюз через API) — это отдельная настройка на стороне телефонии, вне рамок compose-файла. Начинать стоит с почты и чата, телефонию можно подключить позже без пересборки стека.
Бэкапы, обновление и частые проблемы
Данные, которые нужно бэкапить — это volume zammad-postgresql-data (база) и zammad-storage (вложения к тикетам, загруженные файлы). Elasticsearch можно не бэкапить — индекс пересоздаётся командой реиндексации, если база жива.
Простой дамп базы по cron:
docker compose exec -T zammad-postgresql pg_dump -U zammad zammad_production | gzip > /backups/zammad-$(date +%F).sql.gz
Для файлового хранилища подойдёт обычный rsync или tar volume-каталога; частые ошибки такой схемы бэкапа (обрыв при живом контейнере, забытые анонимные volume) разобраны в статье бэкап Docker volume — частые ошибки и решения.
Обновление версии — смена ZAMMAD_VERSION в .env и повторный прогон init-контейнера:
docker compose pull
docker compose up zammad-init
docker compose up -d
Перед апдейтом на мажорную версию обязательно сделайте свежий дамп базы — миграции Zammad необратимы без бэкапа.
Частые проблемы при эксплуатации:
| Симптом | Вероятная причина |
|---|---|
| Поиск по тикетам не работает | Elasticsearch не поднялся или zammad-init не успел создать индексы |
| Интерфейс не обновляется в реальном времени | zammad-websocket недоступен через прокси — проверьте, что Upgrade/Connection заголовки прокидываются |
| Письма не создают тикеты | Неверные IMAP-креды или ящик заблокировал доступ по протоколу IMAP у провайдера |
| После рестарта пропали настройки | Volume zammad-storage/zammad-postgresql-data не был примонтирован как именованный (данные оказались в анонимном volume) |
Контейнер zammad-nginx не видит бэкенд | Сервисы стартовали не в том порядке — не запускайте всё одной командой при первом разворачивании |
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько ресурсов нужно серверу для Zammad?
Для команды до 10-15 агентов хватает 2 vCPU и 4 ГБ RAM — большую часть памяти съедает Elasticsearch. От 20+ агентов и активного потока писем комфортнее на 4 vCPU / 8 ГБ.
Можно ли обойтись без Elasticsearch?
Формально Zammad стартует и без него, но поиск и часть отчётов в интерфейсе перестают работать корректно — экономить на этом компоненте не стоит.
Как перенести Zammad на другой сервер?
Остановить стек, скопировать volume zammad-postgresql-data и zammad-storage (например, через docker run --rm -v ... tar), поднять тот же compose на новом сервере с теми же версиями образов.
Нужен ли отдельный домен под Zammad?
Не обязательно, но удобнее — поддомен вроде helpdesk.example.com проще защитить отдельным SSL-сертификатом и не путает пользователей с основным сайтом.
Что делать, если zammad-init завершился с ошибкой миграции?
Смотрите последние строки лога командой docker compose logs zammad-init — чаще всего это несовпадение версии образа приложения и версии, которая уже накатана в базе; не мешайте теги разных релизов.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →