Як розгорнути Actual Budget на власному VPS
Інструкція з розгортання Actual Budget через Docker Compose: том даних, обов’язковий HTTPS для браузера, перший бюджет, імпорт банківських даних і резервні копії.
Що ви розгортаєте
Actual Budget — це self-hosted застосунок для бюджетування за методом конвертів. Зазвичай його обирають як альтернативу YNAB, яку можна розмістити на власному сервері. Сервер складається з одного контейнера, одного тому даних і одного HTTPS-імені. Усі функції, потрібні для звичайного бюджету, без проблем працюють на найменшому VPS, який можна орендувати, оскільки сервер переважно зберігає файли та синхронізує їх.
Перед введенням команд варто зрозуміти архітектуру. Сам бюджет — це база даних SQLite, яка зберігається у браузері та в кожному мобільному застосунку. Сервер, який ви зараз встановлюватимете, є кінцевою точкою синхронізації. Він зберігає список облікових записів, файли бюджетів і журнал змін, завдяки якому телефон і ноутбук узгоджують дані. Тому застосунок продовжує працювати, коли сервер недоступний. Втрата сервера також не призводить до втрати бюджету, якщо хоча б один клієнт усе ще має його копію.
Навіщо серверу HTTPS
Actual вимагає HTTPS, і це не формальність. Actual потребує функції браузера під назвою SharedArrayBuffer, щоб працювати взагалі. Браузери вмикають її лише тоді, коли сторінка є ізольованою від інших джерел у тому, що специфікація називає безпечним контекстом. Безпечний контекст — це https:// або http://localhost. Якщо відкрити застосунок із http://203.0.113.10:5006 у браузері на іншому пристрої, браузер не передасть цю функцію сторінці, тому бюджет не завантажиться. Для телефонів винятків немає. У FAQ Actual зазначено, що офіційний застосунок для iOS перебуває на етапі бета-тестування та підключається до вашого власного сервера. Офіційного застосунку для Android поки немає. Тому на більшості телефонів використовується той самий вебзастосунок, встановлений на головний екран як Progressive Web App, і те саме правило безпечного контексту також діє.
Є два робочі варіанти. Розмістити справжній сертифікат для справжнього доменного імені перед контейнером — саме це передбачає цей посібник. Або надати серверу самопідписаний сертифікат за допомогою ACTUAL_HTTPS_KEY і ACTUAL_HTTPS_CERT, як описано в документації проєкту, і приймати попередження браузера на кожному пристрої. Безкоштовний сертифікат від Let's Encrypt можна отримати за 5 хвилин, тому оберіть перший варіант.
Встановлення 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, де зберігаються самі файли бюджетів. Змонтуйте цей шлях, інакше наступний 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 означає, що на 127.0.0.1:5006 жоден процес не прийняв підключення. Виконайте docker compose ps --all, оскільки звичайний docker compose ps показує лише запущені контейнери. Якщо контейнер має статус exited, зазвичай причина полягає в проблемі з правами на змонтований том. У журналі це видно за рядком 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. Користувацьких облікових записів за замовчуванням немає: сервер використовує один пароль, тому спільний доступ до бюджету означає спільний доступ до цього пароля. Окремі облікові записи можливі лише після підключення OpenID-провайдера, який Actual підтримує для конфігурацій із кількома користувачами.
Потім створіть файл бюджету. Actual запитає, чи потрібно ввімкнути наскрізне шифрування. Погодьтеся. У такому разі сервер зберігатиме лише шифротекст, що є правильним вибором для фінансових даних на орендованій машині. Це має реальну ціну: пароль шифрування ніколи не передається на сервер. Якщо ви його втратите, файл буде втрачено, і відновити пароль неможливо. Запишіть пароль, перш ніж перейти далі з цього екрана.
Укажіть початкові залишки за поточними даними банківських рахунків, а не імпортуйте історію за кілька років. Конвертне бюджетування працює з коштами, які є у вас зараз, тому відсутність історії не створює проблем.
Імпорт транзакцій
Тут важлива чесність, а не ентузіазм, оскільки саме імпорт є головною причиною, через яку користувачі відмовляються від self-hosted бюджетування.
Ручне введення — базовий варіант, і він працює завжди. Для методу конвертів це, мабуть, і є основна ідея, оскільки введення покупки допомагає звернути на неї увагу.
Імпорт файлів опрацьовує більшість операцій. Actual підтримує CSV, QIF, OFX, QFX і CAMT, а кожен банк експортує щонайменше один із цих форматів. Імпортуйте дані окремо для кожного рахунку з екрана рахунку. Якщо файл має формат CSV, зіставте його стовпці.
Автоматична синхронізація з банками доступна, але для неї потрібен сторонній сервіс, оскільки сервер не може самостійно підключатися до банків. Actual підтримує SimpleFIN Bridge для банків Північної Америки, Enable Banking для Європи, Akahu для Нової Зеландії та Pluggy.ai для Бразилії. GoCardless досі підтримується, але нові облікові записи не приймаються. Зареєструйтеся у провайдера самостійно, згенеруйте облікові дані та додайте їх на сервер. Станом на October 2026 SimpleFIN Bridge стягує 15 доларів США на рік плюс податок за доступ до 25 установ. Інші сервіси мають інші тарифи.
Перед використанням цієї функції потрібно врахувати два обмеження. Облікові дані API зберігаються на сервері та не захищені наскрізним шифруванням, оскільки сервер має їх використовувати. Крім того, синхронізація не виконується, коли 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 — це окрема функція, про яку варто знати, але вона доступна лише в desktop-застосунку. Actual зберігає до 10 останніх копій файла бюджету, доступних через File > Load Backup. Цього достатньо для випадку «я помилково видалив категорію», і сервер для цього не потрібно використовувати. Вебзастосунок у браузері таких копій не зберігає. Тому якщо всі в домі користуються браузером, наведена вище резервна копія сервера — єдина доступна вам копія.
Оновлення сервера
cd /opt/actual
docker compose pull
docker compose up --detachCompose повторно створює контейнер із нового образу та під’єднує той самий volume, тому дані зберігаються. Також оновіть клієнти. Клієнт, версія якого старіша за версію даних, які він отримує, припиняє застосовувати зміни та показує повідомлення Update Required. Перед переходом на нову основну версію створіть резервну копію, оскільки міграції запускаються під час першого старту, а шляху для downgrade немає. Actual поблажливий до плаваючого тегу latest, оскільки його стан зберігається в каталозі файлів. Для застосунку зі справжньою базою даних це не працює. У матеріалі самостійне розгортання Chatwoot описано використання зафіксованих тегів і створення дампа перед оновленням, як того вимагає така практика.
Що виходить з ладу і що ви побачите
Застосунок завантажується, але синхронізація не завершується. Перевірте access log nginx на наявність 413. Це означає, що client_max_body_size має надто низьке значення. 502 натомість означає, що nginx не отримав придатної відповіді від upstream. Найчастіше причина в тому, що контейнер зупинений або не слухає порт 5006, тому спочатку перевірте docker compose ps --all і журнал контейнера.
Застосунок не завантажується на іншому пристрої або повідомляє, що бракує SharedArrayBuffer. Сторінка не працює в захищеному контексті. В адресному рядку буде показано http:// з IP-адресою або hostname, який не є localhost. Виправте сертифікат, а не обходьте цю проблему.
Повідомлення про те, що файл бюджету несумісний із цією версією. Файл відкрито в новішій версії Actual, ніж та, яку ви зараз використовуєте. Оновіть клієнт щонайменше до цієї версії або відновіть резервну копію, створену до того, як новіша версія змінила файл, а потім перезавантажте його.
Контейнер циклічно перезапускається. Прочитайте docker compose logs actual. Помилка доступу до /data означає, що змонтований каталог недоступний для запису користувачу контейнера. Помилка «адреса вже використовується» означає, що інший процес уже використовує 5006 на loopback.
Перше завантаження здається повільним. Під час відкриття браузер завантажує весь файл бюджету. Це одна велика передача, після якої виконуються локальні читання. Проблема не пов’язана з розміром сервера, і збільшення RAM цього не змінить.
FAQ
Чи потрібен Actual Budget HTTPS для роботи?
Так, на практиці. Actual потребує функції браузера SharedArrayBuffer для роботи, а браузери вмикають її лише в захищеному контексті, тобто через https:// або http://localhost. Через звичайний HTTP з іншого комп’ютера застосунок не завантажиться. Офіційний застосунок для iOS проходить бета-тестування, а офіційного застосунку для Android ще немає, тому на більшості телефонів використовується той самий вебзастосунок і діє те саме правило. (FAQ) Використовуйте сертифікат Let's Encrypt для реального hostname або самопідписаний сертифікат із ACTUAL_HTTPS_KEY і ACTUAL_HTTPS_CERT, якщо вас не бентежитиме попередження про сертифікат на кожному пристрої.
Чи може Actual автоматично імпортувати мої банківські транзакції?
Лише через сторонній сервіс, у якому ви самостійно зареєструєтеся: SimpleFIN Bridge у Північній Америці, Enable Banking у Європі, Akahu у Новій Зеландії або Pluggy.ai у Бразилії. GoCardless підтримується, але нові облікові записи більше не приймаються. Ці облікові дані API зберігаються на вашому сервері й не захищені наскрізним шифруванням. Автоматична синхронізація вимкнена за замовчуванням і після ввімкнення працює лише доки Actual відкритий, тому сервер не виконує фонове опитування. Для імпорту CSV, QIF, OFX, QFX і CAMT сторонні сервіси взагалі не потрібні.
Що саме потрібно резервувати?
Змонтований каталог даних, тобто /opt/actual/data у цьому посібнику. Він містить server-files/account.sqlite із даними для входу та сесіями, а також user-files із файлами бюджетів. Перед копіюванням зупиніть контейнер, оскільки копіювання активної бази даних SQLite може зберегти неповний запис. Інші дані на сервері стан не зберігають.
Що станеться, якщо я втрачу пароль шифрування?
Файл неможливо відновити. Пароль ніколи не передається на сервер — у цьому і полягає суть наскрізного шифрування, — тому скинути його неможливо, як і звернутися по допомогу для відновлення. Щойно створите файл, збережіть пароль у менеджері паролів і тримайте копію в місці, яке не залежить від цього самого сервера.
Скільки ресурсів сервера потрібно Actual Budget?
Дуже мало. Контейнер роздає статичні ресурси та файли, а обчислення бюджету виконуються в браузері. Одного спільного vCPU і 1 GB RAM достатньо для стабільної роботи, а каталог даних для домашнього бюджету з історією за кілька років зазвичай займає десятки мегабайтів. Навантаження на диск створюють резервні копії та інші контейнери, а не Actual. Якщо ви підбираєте сервер, на якому паралельно працюватиме щось вимогливіше, мінімальні характеристики зазвичай визначає photo server, тому перед вибором plan перевірте скільки RAM насправді потрібно PhotoPrism та Immich. Та сама логіка діє для media stack: plan визначає transcoding, тоді як браузерний front end на кшталт Halcyon, який перетворює бібліотеку Jellyfin на відеомагазин у стилі 90s, потребує приблизно стільки ж ресурсів, як Actual.