У невеликому офісі задачі живуть усюди, крім одного спільного місця: у листуванні, у голосових, у табличці, яку хтось колись завів, і в голові менеджера, який сьогодні у відпустці. Хто що робить і що вже горить - ніхто цілісно не бачить. А коли справ більшає, це починає коштувати грошей і нервів.
Рішення відоме - спільний таск-менеджер. Але Todoist, Asana чи Trello рахують гроші за кожного учасника й тримають усі ваші плани в чужій хмарі. Vikunja - це self-hosted альтернатива: проєкти, задачі з дедлайнами, канбан-дошки, кілька виглядів на ті самі задачі. Написана на Go, їздить одним невеликим контейнером, за учасників грошей не бере й ваші дані лишає у вас.
Це третій пост серії «Офіс без адміна»: ставимо корисні сервіси так, щоб їх потягнула людина без фаху системного адміністратора. Були вже власний сейф для паролів і самостійний обробник PDF - сьогодні спільний менеджер задач, від порожньої VM до працюючої канбан-дошки.
Що потрібно зробити?#
Підняти Vikunja на одній маленькій віртуалці для офісу:
- винести дані (база + вкладення) на окремий диск - щоб бекапити й переносити їх окремо від системи;
- поставити Docker і запустити Vikunja + Caddy одним
docker compose; - зробити HTTPS без публічного домену через внутрішній центр сертифікації Caddy;
- завести перший акаунт, проєкт і розкласти задачі по канбан-дошці;
- насамкінець замкнути реєстрацію, щоб нові акаунти не заводив хто завгодно.
З чим працюю?#
Vikunja невибаглива, тож віртуалка - скромна:
- 1 vCPU, 1 ГБ RAM;
- 10 ГБ системного диска + окремий 5 ГБ диск під дані;
- ОС Ubuntu 26.04 LTS.
Для SQLite і кількох десятків людей цього з головою. Для певності додамо ще 1 ГБ swap - дешева страховка для машини з гігабайтом пам’яті.
Сервер у прикладі доступний у локальній мережі за адресою 192.168.1.52 - публічного домену в нас поки немає. Усюди далі підставляй свою адресу.
Окремий диск під дані#
Перше правило домашнього (та й не лише домашнього) сервера: дані не повинні лежати там само, де система. Коли база задач і вкладення живуть на окремому диску, їх легко забекапити, збільшити чи перенести на іншу машину - не чіпаючи саму ОС. Тому під Vikunja ми й підкинули віртуалці другий диск на 5 ГБ.
У системі він з’являється порожнім пристроєм /dev/sdb. Форматуємо його в ext4 з міткою vikdata і створюємо точку монтування:
| |
Щоб диск підмонтувався сам після перезавантаження, додаємо його в /etc/fstab - і обов’язково по UUID, а не по /dev/sdb (імена пристроїв можуть помінятися місцями). UUID підкаже blkid:
| |
Рядок у /etc/fstab (підстав свій UUID):
| |
Перевіряємо, що запис коректний, одразу монтуючи все з fstab:
| |
Якщо команда відпрацювала без помилок і /mnt/data змонтувався - диск готовий і переживе перезавантаження.
Поки ми тут, додамо обіцяний 1 ГБ swap:
| |
І теж закріплюємо в /etc/fstab, щоб swap вмикався сам:
| |
Docker з репозиторіїв Ubuntu#
Vikunja запустимо в контейнері - так найпростіше тримати її оновленою й не захламлювати систему. Підключати офіційний репозиторій Docker для нашої задачі зайве: у штатних репозиторіях Ubuntu 26.04 лежить цілком свіжий Docker. Ставимо двома пакетами:
| |
Це дає Docker 29.x і сучасний docker compose версії 2.40.x (саме docker compose, двома словами, а не старий docker-compose). Більше нічого підключати не треба.
Vikunja і Caddy одним compose-файлом#
Складемо весь сервіс в одній папці /opt/vikunja. Там буде три файли: .env із налаштуваннями, docker-compose.yml зі самими контейнерами і Caddyfile для HTTPS. Дані - окремо, на нашому диску:
| |
Маленька, але важлива деталь: права на папку даних#
Контейнер Vikunja працює не від root, а від звичайного користувача з uid 1000. Папка /mnt/data/vikunja, яку ми щойно створили, належить root - і якщо лишити так, Vikunja не зможе створити в ній базу й впаде по колу з помилкою permission denied. Тому одразу віддамо папку тому користувачеві, під яким працює контейнер:
| |
Це типова заковика self-hosted контейнерів, і її легко прогледіти. Зробивши цей крок зараз, ми уникнемо найпоширенішої помилки першого запуску.
Файл .env#
Vikunja цілком налаштовується змінними оточення - жодного конфіга редагувати не треба. Складемо їх в окремий .env:
| |
Кілька слів про кожен рядок:
VIKUNJA_SERVICE_PUBLICURL- точна адреса, за якою користувачі відкриватимуть Vikunja, разом зіhttps://. Фронтенд бере з неї адресу API, тож вона має збігатися з тим, що люди вводять у браузері.VIKUNJA_SERVICE_SECRET- секрет, яким підписуються сесії (токени входу). Згенеруй його один раз випадковим і більше не чіпай:openssl rand -hex 32. Якщо його не задати, Vikunja згенерує тимчасовий, і всіх розлогінюватиме при кожному перезапуску.VIKUNJA_DATABASE_TYPE=sqlite- для офісу на десятки людей SQLite цілком достатньо, і це найпростіший варіант: жодної окремої бази ставити не треба. Для великих команд Vikunja вміє PostgreSQL - але це вже інша історія.VIKUNJA_DATABASE_PATHіVIKUNJA_FILES_BASEPATH- кладемо базу й вкладення всередину/data, а/dataзмонтуємо з нашого окремого диска.VIKUNJA_SERVICE_ENABLEREGISTRATION=true- дозвіл на самостійну реєстрацію. Лишаємо увімкненим тільки поки заводимо перші акаунти, а потім вимкнемо.VIKUNJA_SERVICE_TIMEZONE- часовий пояс, щоб дедлайни й нагадування рахувались правильно.
Раніше секрет сесій звався
VIKUNJA_SERVICE_JWTSECRET. У свіжих версіях ця назва ще працює, але позначена застарілою й лається у лог - тож одразу беремо нову,VIKUNJA_SERVICE_SECRET.
Файл із секретом не повинні читати всі підряд:
| |
docker-compose.yml#
| |
Головне тут: версія Vikunja зафіксована на 2.6.0 (щоб оновлення не прилетіло несподівано разом із міграцією бази), а всі дані лежать на нашому окремому диску - /mnt/data/vikunja усередині контейнера видно як /data. Поруч стоїть Caddy - він прийматиме HTTPS ззовні й передаватиме запити Vikunja на її внутрішній порт 3456.
Caddyfile: HTTPS без домену#
Vikunja технічно працює й по звичайному http. Але сторінка входу, за якою стоять усі задачі компанії, по голому http - погана ідея: пароль летить відкритим текстом. Тож зробимо HTTPS відразу.
Зазвичай сертифікат видає Let’s Encrypt, але для цього потрібен публічний домен. У нас його немає - сервер живе в локальній мережі за IP. Рятує Caddy: він уміє бути власним центром сертифікації й сам видати собі сертифікат. Ось весь Caddyfile:
| |
tls internal і каже Caddy стати власним CA. А default_sni тут не прикраса, а необхідність: коли клієнт стукає по IP-адресі, він не надсилає ім’я сервера (SNI), і Caddy не знає, який сертифікат показувати. Без цього рядка TLS-рукостискання просто обривається - класична заковика. Ми явно кажемо: за замовчуванням показуй сертифікат для 192.168.1.52.
Запускаємо все:
| |
Перевіримо, що Vikunja піднялась. У логах має бути вдала міграція бази й рядок, що сервер слухає порт:
| |
| |
А тоді спитаємо сам сервіс по HTTPS, вказавши curl наш внутрішній кореневий сертифікат:
| |
У відповідь прилетить JSON із версією й налаштуваннями - отже, і Vikunja, і HTTPS працюють:
| |
Роздаємо довіру#
Є один компроміс життя без домену: сертифікат видав наш власний CA, якого браузери поки не знають, тож спершу вони лаятимуться «не захищено». Щоб цього не було, кореневий сертифікат Caddy треба раз установити на офісні пристрої. Лежить він тут:
| |
Цей root.crt розносимо по пристроях команди один раз:
- Linux - покласти в довірені й виконати
update-ca-certificates; - Windows / macOS - додати сертифікат у системне сховище довіри;
- мобільні - встановити як профіль сертифіката.
Коли згодом у офісу з’явиться справжній домен, уся ця морока зникає: у Caddyfile замінюєш IP на доменне ім’я й прибираєш рядок tls internal - Caddy сам візьме безкоштовний сертифікат у Let’s Encrypt, і розносити нічого не доведеться.
Перший вхід і проєкт#
Відкриваємо https://192.168.1.52 у браузері. Поки VIKUNJA_SERVICE_ENABLEREGISTRATION=true, на сторінці входу є посилання на реєстрацію - заводимо собі акаунт власника.
Усередині все крутиться навколо проєктів. Проєкт - це список задач з однієї теми: «Запуск нового сайту», «Закупівлі», «Онбординг новачка». Проєкти можна вкладати один в одного й ділити з колегами. Заводимо перший і наповнюємо його задачами - коротко, по одній справі на задачу.
Кожна задача - це не просто рядок тексту. Їй можна поставити дедлайн, пріоритет, виконавця, мітки, розбити на підзадачі, причепити файл чи обговорити в коментарях. Для офісу цього більш ніж достатньо, щоб не губити домовленості.
Канбан: видно, хто що робить#
Найкорисніше у Vikunja - що на ті самі задачі можна дивитися по-різному. Один проєкт одразу має кілька виглядів, і перемикаються вони вгорі:
- Список - звичний плаский перелік, добре для «просто занотувати».
- Дошка - канбан із колонками, де задачі - це картки, які перетягуєш мишею.
- Таблиця - задачі рядками з колонками-полями, зручно фільтрувати й сортувати.
- Розпорядок - задачі на часовій шкалі (Gantt), видно, що з чим перетинається в часі.
Для невеликої команди найнаочніший - канбан-дошка. Колонки на кшталт «Треба зробити -> У роботі -> Готово», а картки їздять між ними в міру роботи. На кожній картці одразу видно дедлайн і пріоритет. Один погляд на дошку - і зрозуміло, де затик і хто чим зайнятий, без жодної планерки.

Ось як це виглядає живцем - від входу до дошки й картки задачі:
Команда: запрошення й ролі#
Щоб задачі стали спільними, треба додати людей. Vikunja дає два шляхи:
- Поділитися проєктом - відкрити конкретний проєкт окремому користувачеві або цілій команді (Teams), з правами «тільки читати», «писати» чи «адмініструвати». Так один проєкт бачить лише той, кому він потрібен.
- Команди (Teams) - згрупувати людей раз (скажімо, «Маркетинг») і далі ділити проєкти з усією групою, а не з кожним окремо.
Один нюанс саме для самостійного сервера. Ми не налаштовували SMTP, тому Vikunja не надсилатиме листів - ні запрошень, ні нагадувань на пошту. Поки пошти немає, акаунти найпростіше заводити з командного рядка, прямо в контейнері:
| |
Тож налаштування SMTP - перше, що варто додати згодом: тоді й запрошення, і нагадування про дедлайни приходитимуть листами самі.
Замикаємо реєстрацію#
Поки ми заводили акаунти, реєстрація була відкрита - і це дірка: будь-хто, хто дотягнеться до сервера, міг би собі завести акаунт. Коли команда на місці, реєстрацію треба закрити. У .env міняємо:
| |
і перезастосовуємо:
| |
Тепер нові акаунти заводить тільки адмін - тією самою командою vikunja user create. Двері замкнені.
Бекап#
Усе цінне лежить у /mnt/data/vikunja: база vikunja.db і папка files із вкладеннями. Живу базу SQLite не варто копіювати звичайним cp під час роботи, тож Vikunja має власну команду, яка знімає узгоджений дамп - база, вкладення й конфіг одним zip-файлом:
| |
Zip з’явиться в /mnt/data/vikunja, звідки його варто щоночі відправляти на NAS чи в інше місце поза сервером. Відновлюється все дзеркальною командою vikunja restore <файл>. Детальніше розкладати схему бекапу будемо окремо - поки що головне, щоб нічна копія просто була.
Куди далі#
Маємо власний менеджер задач для команди: один раз розгорнули, роздали доступи - і офіс більше не ганяє справи по чатах. Наступні очевидні кроки, коли буде настрій: SMTP для запрошень і нагадувань листами, справжній домен замість IP (і Let’s Encrypt замість роздачі сертифіката), а як команда дуже виросте - переїзд із SQLite на PostgreSQL. Vikunja також уміє імпортувати задачі з Todoist, Trello й TickTick - якщо переїжджаєте з них, перенести напрацьоване не доведеться руками.
Якщо ж уся ця історія з «зроби сам» - не твоє, і хочеться, щоб хтось просто розгорнув і наглядав за цим замість тебе, напиши в teneta.tech - підкажуть і візьмуть на себе.
А серію «Офіс без адміна» продовжимо: цеглинка за цеглинкою зберемо офісну інфраструктуру, яку потягне звичайна людина без системного адміністратора за плечима.



