Kimai в Docker Compose: готовый файл
Toggl и Clockify удобны, пока не упираешься в лимиты бесплатного тарифа или не считаешь, сколько за год набегает на подписку для команды из десяти человек. Kimai — open-source тайм-трекер с теми же возможностями (проекты, клиенты, отчёты, инвойсы, API) плюс собственный сервер и никаких ограничений по числу записей. Ниже — рабочий docker-compose.yml с MySQL, который поднимается за несколько минут, и разбор нюансов, которые обычно всплывают уже после первого запуска.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Kimai и когда имеет смысл self-hosted
Kimai — веб-приложение для учёта рабочего времени на Symfony: таймер в браузере или мобильном приложении, привязка записей к клиентам/проектам/активностям, командные отчёты, экспорт в PDF/Excel, генерация инвойсов прямо из отработанных часов. Есть встроенный REST API, права доступа по ролям, тегам и командам, а также плагины для расширения функциональности.
Для фрилансера или небольшой студии self-hosted вариант оправдан по трём причинам:
- Нет лимита на пользователей и записи. Бесплатные тарифы SaaS-трекеров обычно режут либо число участников, либо историю, либо отчёты — self-hosted версия ограничена только ресурсами вашего сервера.
- Данные клиентов остаются у вас. Если вы ведёте учёт часов по NDA-проектам, хранить эти данные в чужом облаке — лишний риск, особенно когда среди клиентов есть требовательные к приватности заказчики.
- Инвойсинг без посредника. Kimai умеет генерировать счета из отработанных часов по шаблонам — не нужно выгружать данные в отдельный биллинговый сервис и сверять цифры вручную.
Минус стандартный для любого self-hosted инструмента: обновления, бэкапы БД и HTTPS теперь ваша ответственность. Docker Compose закрывает установку и апдейты, остальное разбираем по шагам ниже.
Готовый docker-compose.yml с MySQL
Kimai официально распространяется как Docker-образ kimai/kimai в двух вариантах — apache (веб-сервер уже внутри контейнера) и fpm-alpine (только PHP-FPM, для тех, у кого свой nginx перед ним). Для быстрого старта проще вариант apache — минимум движущихся частей.
version: "3.8"
services:
mysql:
image: mysql:8.0
container_name: kimai_mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: "замените-на-случайный-пароль"
MYSQL_DATABASE: "kimai"
MYSQL_USER: "kimai"
MYSQL_PASSWORD: "замените-на-случайный-пароль"
volumes:
- kimai_db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 15s
timeout: 5s
retries: 10
kimai:
image: kimai/kimai:apache
container_name: kimai
restart: unless-stopped
depends_on:
mysql:
condition: service_healthy
environment:
DATABASE_URL: "mysql://kimai:замените-на-случайный-пароль@mysql/kimai?charset=utf8mb4&serverVersion=8.0"
TRUSTED_HOSTS: "^(kimai\\.example\\.com|localhost)$$"
ADMINMAIL: "admin@example.com"
ADMINPASS: "замените-на-надёжный-пароль"
APP_ENV: "prod"
ports:
- "127.0.0.1:8001:8001"
volumes:
- kimai_data:/opt/kimai/var
volumes:
kimai_db:
kimai_data:
Поднимаем:
mkdir -p ~/kimai && cd ~/kimai
# вставляем docker-compose.yml из примера выше
docker compose up -d
docker compose logs -f kimai
При первом запуске контейнер сам прогоняет миграции базы и создаёт администратора с почтой и паролем из ADMINMAIL/ADMINPASS — эти переменные работают только при пустой базе, повторный запуск с уже мигрированной схемой их игнорирует. Первый вход — по адресу контейнера на порту 8001, логин и пароль администратора те, что указали в переменных.
Образ обновляется регулярно — перед стартом на проде проверьте на Docker Hub, какие теги версий сейчас доступны, и зафиксируйте конкретный тег вместо :apache без версии: так контейнер не подтянет неожиданно мажорное обновление при простом docker compose pull.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПеременные окружения, на которые стоит обратить внимание
| Переменная | Назначение | Частая ошибка |
|---|---|---|
DATABASE_URL | DSN подключения к MySQL/MariaDB в формате Doctrine | опечатка в пароле или имени базы — контейнер падает в рестарт-луп, смотрите docker compose logs kimai |
TRUSTED_HOSTS | регулярное выражение допустимых значений заголовка Host | если домен не совпадает с regex — Symfony отдаёт 400 Bad Request на любой запрос |
ADMINMAIL / ADMINPASS | учётка первого администратора | работают только на пустой базе; после первого старта менять пароль нужно уже из интерфейса |
APP_ENV | режим Symfony (prod/dev) | dev заметно медленнее и не предназначен для продакшена |
MAILER_DSN | подключение к SMTP для уведомлений и приглашений | без него не работает восстановление пароля и оповещения по проектам |
Для писем добавьте в блок environment сервиса kimai:
MAILER_DSN: "smtp://user:password@smtp.example.com:587"
Все пароли и DSN лучше вынести в отдельный .env-файл рядом с docker-compose.yml, а в самом compose-файле ссылаться через ${VAR} — это упрощает ротацию секретов и не даёт случайно закоммитить их в git, если конфиг лежит в репозитории.
Каталог var: что там лежит и почему его нельзя терять
Внутри контейнера /opt/kimai/var — это единственное место, где Kimai хранит то, что не лежит в базе: загруженные логотипы компании и клиентов, сгенерированные PDF-инвойсы, кэш и логи приложения, а также установленные вручную плагины. Без volume на этот путь весь этот слой данных исчезает при пересоздании контейнера — база останется цела, но логотипы в счетах и история сгенерированных PDF пропадут.
В примере выше это уже учтено через kimai_data:/opt/kimai/var. Проверить, что volume реально примонтирован и не пустой после первого запуска:
docker compose exec kimai ls -la /opt/kimai/var
Если разворачиваете fpm-alpine-вариант со своим nginx впереди, путь внутри контейнера тот же — меняется только то, что отдаёт статику и проксирует PHP-FPM.
HTTPS через reverse proxy
Образ kimai/kimai:apache сам HTTPS не выпускает — отдаёт обычный HTTP на порту 8001, а сертификат добывает внешний reverse proxy. Если на сервере уже настроен Caddy с автоматическим SSL, добавляете блок:
kimai.example.com {
reverse_proxy 127.0.0.1:8001
}
Caddy сам выпустит и продлит сертификат Let's Encrypt, как только А-запись домена указывает на IP сервера, а порты 80/443 свободны. Такая схема удобна, если на одном сервере крутится несколько self-hosted инструментов — под каждый не нужен отдельный IP, только свой поддомен и блок в Caddyfile.
Не забудьте синхронизировать TRUSTED_HOSTS в переменных Kimai с реальным доменом — иначе после переключения на HTTPS через прокси интерфейс начнёт отдавать 400 ошибку на все запросы, потому что заголовок Host не совпадёт с регулярным выражением.
Настройка команды: клиенты, проекты, активности
Структура данных в Kimai трёхуровневая: клиент → проект → активность, и время всегда логируется на конкретную активность внутри проекта. Это удобно для агентств и фрилансеров, которые ведут несколько клиентов параллельно — отчёт по клиенту сразу показывает разбивку по проектам и типам работ.
Базовая настройка после первого входа:
- Создать клиента (Customers → New Customer) — минимум название, по желанию адрес и реквизиты для будущих инвойсов.
- Создать проект внутри клиента, указать бюджет по часам или деньгам, если нужен контроль перерасхода.
- Добавить активности — типы работ (разработка, дизайн, встречи, поддержка), общие для всех проектов или привязанные к конкретному.
- Пригласить пользователей (Users → New User) и назначить роли: обычный пользователь видит и логирует только своё время, тимлид — время команды, администратор управляет настройками.
Тайм-трекинг доступен в трёх режимах: живой таймер в интерфейсе (старт/стоп), ручной ввод интервала задним числом и мобильные приложения (официальные под iOS/Android общаются с сервером через тот же REST API, что доступен и для сторонних интеграций).
Командам, которые хотят автоматизировать отчётность, стоит посмотреть встроенный REST API (/api/doc на вашем инстансе показывает Swagger-документацию) — через него можно выгружать тайм-логи в внешние BI-инструменты или синхронизировать с системой биллинга без ручного экспорта Excel.
Бэкапы и обновление версии
Бэкапить нужно два независимых слоя: базу MySQL и содержимое /opt/kimai/var (логотипы, PDF-инвойсы, плагины).
Дамп базы:
docker compose exec mysql mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" kimai | gzip > kimai_$(date +%F).sql.gz
Восстановление на чистую базу:
gunzip -c kimai_2026-08-20.sql.gz | docker compose exec -T mysql mysql -u root -p"${MYSQL_ROOT_PASSWORD}" kimai
Каталог var достаточно копировать как обычную директорию тем же архиватором, что и остальные volume'ы на сервере — если уже настроен регулярный процесс, логично встроить Kimai в него, а не заводить отдельный скрипт. Практику дампов MySQL и типовые грабли разбирали отдельно в статье про бэкап MySQL на сервере, а общий подход к бэкапу docker volume — в статье про бэкап Docker volume.
Обновление — стандартный для Docker Compose цикл:
docker compose pull kimai
docker compose up -d kimai
docker compose logs -f kimai
Kimai сам применяет миграции схемы при старте новой версии. Перед обновлением через несколько мажорных версий сразу — обязательно свежий дамп базы: если миграция упадёт на середине, откатиться на бэкап быстрее, чем разбираться с частично применённой схемой вручную.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли использовать PostgreSQL вместо MySQL?
Kimai официально поддерживает MySQL/MariaDB как основную СУБД, DSN в DATABASE_URL рассчитан именно на них. Формально Doctrine DBAL умеет и PostgreSQL, но это неподдерживаемый производителем сценарий — для продакшена безопаснее держать MySQL или MariaDB, как в примере выше.
Как сбросить пароль администратора, если доступ потерян?
Через консоль внутри контейнера: docker compose exec kimai /opt/kimai/bin/console kimai:reset-password admin@example.com — команда запросит новый пароль интерактивно и применит его напрямую в базе.
Нужен ли Redis для Kimai?
Нет, в отличие от многих self-hosted инструментов Kimai не требует Redis — кэш и сессии по умолчанию работают на файловой системе внутри /opt/kimai/var, чего достаточно даже для команд в несколько десятков человек.
Можно ли настроить оплату/инвойсы в разных валютах?
Да, валюта задаётся на уровне клиента или проекта, а шаблоны инвойсов поддерживают несколько языков и форматов вывода (PDF, HTML, XLSX) — настраивается в разделе Invoices после создания клиентов с нужной валютой.
Работают ли официальные мобильные приложения с self-hosted сервером?
Да, мобильные клиенты Kimai подключаются к любому серверу по URL и API-токену — при первом входе в приложении просто указываете адрес своего инстанса вместо облачного, дальше синхронизация идёт как обычно.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →