Як розгорнути Actual Budget на власному VPS
Інструкція з розгортання Actual Budget через Docker Compose: том даних, обов’язковий HTTPS для браузера, перший бюджет, імпорт банківських даних і резервні копії.
Що ви розгортаєте
Actual Budget — це self-hosted застосунок для бюджетування за методом конвертів. Зазвичай його обирають як альтернативу YNAB, яку можна розмістити на власному сервері. Сервер складається з одного контейнера, одного тому даних і одного HTTPS-імені. Усі функції, потрібні для звичайного бюджету, без проблем працюють на найменшому VPS, який можна орендувати, оскільки сервер переважно зберігає файли та синхронізує їх.
Перед введенням команд варто зрозуміти архітектуру. Сам бюджет — це база даних SQLite, яка зберігається у браузері та в кожному мобільному застосунку. Сервер, який ви зараз встановлюватимете, є кінцевою точкою синхронізації. Він зберігає список облікових записів, файли бюджетів і журнал змін, завдяки якому телефон і ноутбук узгоджують дані. Тому застосунок продовжує працювати, коли сервер недоступний. Втрата сервера також не призводить до втрати бюджету, якщо хоча б один клієнт усе ще має його копію.
Чому серверу потрібен HTTPS
Actual вимагає HTTPS, і це не формальність. Браузери надають Web Crypto API — інтерфейс, який Actual використовує для наскрізного шифрування, — лише в контексті, який специфікація називає захищеним. Захищений контекст — це https:// або http://localhost. Якщо відкрити застосунок із http://203.0.113.10:5006 у браузері на іншому комп’ютері, ці можливості будуть недоступні, оскільки браузер не передав їх вебсторінці. Офіційні мобільні збірки також відхиляють звичайну URL-адресу сервера http://.
Тому є два робочі варіанти. Розмістити перед контейнером справжній сертифікат для справжнього доменного імені — саме це описано в цьому посібнику. Або надати серверу самопідписаний сертифікат за допомогою ACTUAL_HTTPS_KEY і ACTUAL_HTTPS_CERT, як описано в документації проєкту, і приймати попередження браузера на кожному пристрої. Безкоштовний сертифікат від Let's Encrypt можна отримати за п’ять хвилин, тому оберіть перший варіант.
Встановлення Actual Budget за допомогою Docker Compose
Спочатку встановіть Docker, якщо сервер щойно розгорнуто. Якщо синтаксис файлів Compose вам незнайомий, у посібнику Основи Docker Compose для VPS описано поля, використані нижче.
sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/dataСтворіть файл /opt/actual/docker-compose.yml:
services:
actual:
image: actualbudget/actual-server:latest
container_name: actual
restart: unless-stopped
ports:
- '127.0.0.1:5006:5006'
volumes:
- ./data:/dataУ цьому файлі важливі три деталі.
Образ має назву actualbudget/actual-server:latest. Проєкт публікує його в Docker Hub і дзеркалі за адресою ghcr.io/actualbudget/actual. Для малопотужних машин є тег latest-alpine.
Контейнер записує всі дані в /data. Усередині нього є server-files, де зберігається account.sqlite з вашими обліковими даними та токенами сеансів, а також user-files, де зберігаються самі файли бюджету. Підключіть цей шлях як volume, інакше наступний docker compose pull видалить ваш бюджет. ACTUAL_DATA_DIR дає змогу змінити його, але стандартне значення підходить.
Порт опубліковано лише на 127.0.0.1. Запис без адреси 5006:5006 публікує порт на всіх інтерфейсах. Docker додає власні правила перед ufw, тому застосунок буде доступний з інтернету навіть за наявності firewall із політикою заборони всіх підключень. Це неочікуване поводження пояснено в матеріалі чому опубліковані Docker порти обходять ufw. Прив’язка до loopback означає, що доступ до застосунку матиме лише reverse proxy на цьому самому сервері.
Запустіть його:
cd /opt/actual
docker compose up --detach
docker compose logs -f actualЖурнал стабілізується, коли сервер повідомить, що прослуховує порт 5006. Перевірте його локально, перш ніж налаштовувати DNS:
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/Відповідь 200 означає, що застосунок обробляє запити. Відповідь curl: (7) Failed to connect означає, що контейнер не запущений, а docker compose ps покаже, що він завершив роботу. Зазвичай причина полягає в проблемі з правами доступу до підключеного тому. У журналі це буде видно як рядок EACCES.
Налаштуйте сертифікат і справжнє ім’я спереду
Спрямуйте A-запис на VPS, budget.example.com, і дочекайтеся його розповсюдження. Потім інсталюйте nginx і випустіть сертифікат. У посібнику Certbot в Ubuntu 24.04 з nginx докладно описано випуск сертифіката й таймер його поновлення.
Блок проксі:
server {
listen 443 ssl;
http2 on;
server_name budget.example.com;
ssl_certificate /etc/letsencrypt/live/budget.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/budget.example.com/privkey.pem;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:5006;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}client_max_body_size — це рядок, про який часто забувають. Під час повної синхронізації файл бюджету завантажується цілком. За замовчуванням nginx дозволяє тіло запиту розміром до 1 MB, тому після перевищення цього розміру синхронізація завершується помилкою 413 Request Entity Too Large у журналі доступу nginx, а застосунок показує лише загальну помилку синхронізації. Сервер має окремі обмеження: ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB за замовчуванням дорівнює 20, а ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB — 50, тому встановіть для nginx значення, що перевищує відповідне обмеження.
Перезавантажте конфігурацію та перевірте результат:
sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/Перший запуск: пароль і перший файл бюджету
Відкрийте https://budget.example.com у браузері. На першому екрані потрібно встановити пароль сервера. Цей пароль захищає весь сервер, тому згенеруйте довгий випадковий пароль і збережіть його там, де зможете знову його знайти, наприклад у self-hosted менеджері паролів Vaultwarden. Створювати облікові записи користувачів не потрібно. Сервер Actual за задумом використовує один пароль, тому спільний доступ до бюджету означає спільний доступ до цього пароля.
Потім створіть файл бюджету. Actual запитає, чи потрібно ввімкнути наскрізне шифрування. Виберіть «Так». У такому разі сервер зберігатиме лише шифротекст, що є правильним рішенням для фінансових даних на орендованій машині. Це має реальну ціну: пароль шифрування ніколи не передається на сервер. Якщо ви його втратите, файл буде втрачено, а скинути пароль неможливо. Запишіть пароль до того, як перейдете далі з цього екрана.
Укажіть початкові залишки за поточними даними банку, а не імпортуйте історію за кілька років. Бюджетування за методом конвертів працює з коштами, які є у вас зараз, тому відсутність історії нічого вам не коштує.
Імпорт транзакцій
Тут важливіша чесність, ніж ентузіазм, оскільки саме імпорт є основною причиною, через яку користувачі відмовляються від self-hosted бюджетування.
Ручне введення — базовий варіант, і він працює завжди. Для envelope-методу це, можливо, навіть головна ідея, адже введення покупки змушує звернути на неї увагу.
Імпорт файлів обробляє більшість даних. Actual читає CSV, QIF, OFX і QFX, а кожен банк експортує принаймні один із цих форматів. Імпортуйте дані для кожного рахунку з екрана рахунку, один раз зіставте стовпці, і Actual запам’ятає цю схему для рахунку.
Автоматична синхронізація з банками існує, але для неї потрібен сторонній сервіс, оскільки сервер не може самостійно підключатися до банків. Actual підтримує SimpleFIN Bridge для банків Північної Америки, Enable Banking для Європи, Akahu для Нової Зеландії та Pluggy.ai для Бразилії. GoCardless досі підтримується, але нові облікові записи більше не приймаються. Ви самостійно реєструєтеся в постачальника, створюєте облікові дані та додаєте їх на сервер. Станом на July 2026 SimpleFIN Bridge стягує 15 US dollars на рік за доступ до 25 установ, а в інших сервісів інші тарифи.
Перш ніж покладатися на цю можливість, прийміть два обмеження. Облікові дані API зберігаються на сервері й не захищені end-to-end encryption, оскільки сервер має використовувати їх. Actual не виконує опитування: синхронізацію запускають натисканням кнопки, а не фоновим завданням.
Резервні копії, тому що це лише файли
Усе важливе зберігається в /opt/actual/data. Експорт не потрібен, і дамп бази даних створювати не потрібно.
Єдина особливість — SQLite. Копіювання account.sqlite, коли сервер записує до нього дані, може зберегти незавершену транзакцію. Це стане зрозуміло лише під час відновлення. Зупиніть контейнер на кілька секунд, потрібних для копіювання:
cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose startДодайте це до розкладу за підходом із матеріалу резервне копіювання restic на VPS. Там описано налаштування репозиторію, політику зберігання та перевірку відновлення. Виконайте перевірку відновлення. Резервна копія, з якої ви ніколи не виконували відновлення, є лише припущенням.
Власні резервні копії Actual на стороні клієнта — окрема функція, про яку варто знати. Браузер зберігає останні копії файлу бюджету. Вони доступні з меню файлу та дають змогу виправити ситуацію «я випадково видалив категорію», не змінюючи сервер.
Оновлення сервера
cd /opt/actual
docker compose pull
docker compose up --detachCompose повторно створює контейнер із нового образу та під’єднує той самий volume, тому дані зберігаються. Оновіть також клієнти. Версії сервера й застосунку мають залишатися близькими. Клієнт, який значно старіший за сервер, може відмовитися синхронізуватися й показати повідомлення про невідповідність версій. Перед переходом на нову основну версію створіть резервну копію, оскільки під час першого запуску виконуються міграції, а можливості повернутися на попередню версію немає. Actual допускає використання плаваючого тегу latest, оскільки його стан зберігається як каталог файлів. Застосунок зі справжньою базою даних так не працює. У матеріалі self-hosting Chatwoot описано використання фіксованих тегів і створення дампа перед оновленням, як це рекомендовано.
Що ламається і що ви побачите
Застосунок завантажується, але синхронізація не завершується. Перевірте журнал доступу nginx на наявність 413. Це означає, що client_max_body_size встановлено надто низько. Натомість 502 означає, що nginx запущений, а контейнер — ні.
Параметри шифрування відсутні або мобільний застосунок відхиляє URL. Сторінка не працює в безпечному контексті. В адресному рядку буде показано http:// з IP-адресою або іменем хоста, яке не є localhost. Виправте сертифікат, а не шукайте обхідний шлях.
З’являється повідомлення, що файл бюджету несумісний із цією версією. Версії клієнта й сервера відрізняються. Оновіть обидва компоненти до однієї версії та перезавантажте сторінку.
Контейнер циклічно перезапускається. Перегляньте docker compose logs actual. Помилка доступу до /data означає, що змонтований каталог недоступний для запису користувачу контейнера. Помилка «адреса вже використовується» означає, що порт 5006 на loopback уже зайнятий іншим процесом.
Перше завантаження здається повільним. Під час відкриття файлу бюджету весь файл завантажується в браузер. Це одна велика передача, після якої виконуються локальні операції читання. Проблема не пов’язана з розміром сервера, і збільшення обсягу RAM її не змінить.
FAQ
Чи потрібен Actual Budget HTTPS для роботи?
Так, на практиці потрібен. Наскрізне шифрування Actual використовує Web Crypto API браузера, а браузери надають цей API лише в захищеному контексті, тобто через https:// або http://localhost. Через звичайний HTTP з іншого комп’ютера ці функції недоступні, а офіційні мобільні застосунки відхиляють URL сервера зі звичайним HTTP. Використовуйте сертифікат Let's Encrypt для реального доменного імені або самопідписаний сертифікат із ACTUAL_HTTPS_KEY і ACTUAL_HTTPS_CERT, якщо користуєтеся лише браузером на комп’ютері.
Чи може Actual автоматично імпортувати транзакції з мого банку?
Лише через сторонній сервіс, у якому ви самостійно реєструєтеся: SimpleFIN Bridge у Північній Америці, Enable Banking у Європі, Akahu у Новій Зеландії або Pluggy.ai у Бразилії. GoCardless підтримується, але нові облікові записи більше не приймаються. Ці облікові дані API зберігаються на вашому сервері й не захищені наскрізним шифруванням. Синхронізація також виконується вручну: ви натискаєте кнопку, а фонове опитування не виконується. Для імпорту CSV, QIF, OFX і QFX сторонні сервіси взагалі не потрібні.
Що саме потрібно резервувати?
Змонтований каталог даних, тобто /opt/actual/data у цьому посібнику. Він містить server-files/account.sqlite з обліковими даними та сеансами і user-files із файлами бюджету. Перед копіюванням зупиніть контейнер, оскільки копіювання активної бази даних SQLite може захопити неповний запис. Жодні інші дані на сервері не містять стану застосунку.
Що станеться, якщо я втрачу пароль шифрування?
Файл неможливо відновити. Пароль ніколи не передається на сервер — у цьому і полягає призначення наскрізного шифрування. Тому скидання пароля немає, як і способу відновлення через службу підтримки. Щойно створите файл, збережіть пароль у менеджері паролів і тримайте копію в місці, яке не залежить від цього самого сервера.
Скільки ресурсів сервера потрібно Actual Budget?
Дуже мало. Контейнер обслуговує статичні ресурси та файли, а розрахунки бюджету виконуються у браузері. Actual без проблем працює на одному спільному vCPU і 1 GB RAM, а каталог даних для домашнього бюджету з історією за кілька років зазвичай займає десятки мегабайтів. Навантаження на диск створюють ваші резервні копії та інші контейнери, а не Actual. Якщо ви підбираєте сервер, на якому паралельно працюватиме ресурсомісткіший застосунок, мінімальні вимоги зазвичай визначає фотосервер. Перед вибором тарифу перевірте скільки RAM насправді потрібно PhotoPrism та Immich.