نصب Actual Budget روی VPS با Docker Compose
راهنمای میزبانی Actual Budget روی VPS با Docker Compose؛ از volume داده و HTTPS تا ساخت نخستین بودجه، وارد کردن تراکنشهای بانکی و پشتیبانگیری.
آنچه میسازید
Actual Budget یک برنامه بودجهبندی پاکتی با میزبانی شخصی است و معمولاً وقتی افراد بهدنبال جایگزینی برای YNAB هستند که بتوانند خودشان میزبانی کنند، این برنامه را انتخاب میکنند. سرور شامل یک کانتینر، یک volume داده و یک نام HTTPS است. تمام قابلیتهای موردنیاز برای یک بودجه معمولی روی کوچکترین VPS قابل اجاره نیز بهراحتی اجرا میشوند، زیرا سرور عمدتاً فایلها را ذخیره و آنها را همگامسازی میکند.
پیش از وارد کردن هر چیزی، بهتر است معماری را درک کنید. خود بودجه یک پایگاه داده SQLite است که در مرورگر شما و در هر برنامه موبایل قرار دارد. سروری که در ادامه نصب میکنید، یک endpoint همگامسازی است: فهرست حسابها، فایلهای بودجه و گزارش تغییرات را نگه میدارد تا تلفن و لپتاپ بتوانند وضعیت یکسانی داشته باشند. به همین دلیل، برنامه هنگام ازکارافتادن سرور همچنان کار میکند و به همین دلیل، از دست دادن سرور باعث از دست رفتن بودجه شما نمیشود، مشروط بر اینکه حداقل یک client همچنان یک نسخه از آن را داشته باشد.
چرا سرور به HTTPS نیاز دارد
Actual به HTTPS نیاز دارد و این موضوع صرفاً تشریفاتی نیست. مرورگرها فقط در چیزی که مشخصات فنی آن را زمینه امن مینامد، Web Crypto API را در اختیار صفحه قرار میدهند؛ این همان واسطی است که Actual برای رمزنگاری سرتاسری از آن استفاده میکند. زمینه امن، https:// یا http://localhost است. اگر برنامه را از http://203.0.113.10:5006 در مرورگری روی رایانهای دیگر بارگذاری کنید، این قابلیتها اصلاً در دسترس نیستند؛ زیرا مرورگر هرگز آنها را به صفحه ارائه نکرده است. نسخههای رسمی موبایل نیز URL سرور معمولی http:// را رد میکنند.
بنابراین، 2 راهاندازی عملی وجود دارد. یک گواهی معتبر را روی یک نام واقعی، در مقابل container، قرار دهید؛ این همان کاری است که این راهنما انجام میدهد. یا با استفاده از ACTUAL_HTTPS_KEY و ACTUAL_HTTPS_CERT، یک گواهی self-signed برای سرور ایجاد کنید؛ این روش در مستندات پروژه توضیح داده شده است، اما باید در هر دستگاه هشدار مرورگر را بپذیرید. دریافت یک گواهی رایگان از 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سه جزئیات این فایل اهمیت دارند.
image برابر با actualbudget/actual-server:latest است که پروژه آن را در Docker Hub منتشر کرده و در ghcr.io/actualbudget/actual نیز mirror کرده است. برای ماشینهای کممصرف، tag برابر با latest-alpine وجود دارد.
container همه موارد را در /data مینویسد. در این مسیر، server-files شامل account.sqlite با اطلاعات ورود و tokenهای session است و user-files خود فایلهای budget را نگه میدارد. این مسیر را mount کنید؛ در غیر این صورت اجرای بعدی docker compose pull فایلهای budget شما را حذف میکند. ACTUAL_DATA_DIR میتواند آن را جابهجا کند، اما مقدار پیشفرض مناسب است.
port فقط روی 127.0.0.1 منتشر میشود. یک 5006:5006 ساده روی همه interfaceها منتشر میکند و Docker قوانین خودش را پیش از ufw اعمال میکند؛ بنابراین برنامه حتی با firewall دارای سیاست deny-all نیز در اینترنت باز خواهد بود. این رفتار غیرمنتظره در راهنمای دلیل عبور portهای منتشرشده توسط Docker از ufw توضیح داده شده است. اتصال به loopback باعث میشود فقط reverse proxy روی همان سرور بتواند به آن دسترسی پیدا کند.
آن را اجرا کنید:
cd /opt/actual
docker compose up --detach
docker compose logs -f actualپس از آنکه سرور اعلام کرد روی port 5006 در حال listening است، log پایدار میشود. پیش از تغییر DNS، آن را بهصورت محلی بررسی کنید:
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/مقدار 200 یعنی برنامه در حال service است. مقدار curl: (7) Failed to connect یعنی container در حال اجرا نیست و docker compose ps نشان میدهد که خارج شده است. علت معمول، مشکل permission در volume mountشده است که بهصورت یک خط EACCES در log دیده میشود.
گواهی و نام واقعی را در جلو قرار دهید
یک رکورد A را به VPS، budget.example.com، اشاره دهید و منتظر بمانید تا resolve شود. سپس nginx را نصب کنید و گواهی را صادر کنید. راهنمای Certbot در Ubuntu 24.04 با nginx صدور گواهی و timer تمدید آن را بهطور کامل پوشش میدهد.
بلوک proxy:
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 خطی است که افراد معمولاً فراموش میکنند. در یک full sync، فایل budget بهطور کامل upload میشود. مقدار پیشفرض nginx برای request body برابر با 1 MB است؛ بنابراین وقتی حجم فایل از این مقدار بیشتر شود، sync با 413 Request Entity Too Large در nginx access log شکست میخورد، درحالیکه برنامه فقط یک خطای عمومی sync نشان میدهد. سرور محدودیتهای جداگانهای دارد: مقدار پیشفرض ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB برابر با 20 و مقدار پیشفرض ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB برابر با 50 است؛ بنابراین limit مربوط به nginx را بیشتر از هرکدام از این مقادیر که برای شما اعمال میشود تنظیم کنید.
Reload و test کنید:
sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/اولین اجرا: گذرواژه و نخستین فایل بودجه
https://budget.example.com را در مرورگر باز کنید. در صفحه نخست از شما خواسته میشود گذرواژه سرور را تعیین کنید. این گذرواژه از کل سرور محافظت میکند؛ بنابراین یک گذرواژه تصادفی و طولانی ایجاد کنید و آن را در مکانی نگه دارید که دوباره بتوانید پیدایش کنید؛ برای نمونه، در یک مدیر گذرواژه Vaultwarden خودمیزبان. نیازی به ایجاد حساب کاربری نیست. سرور Actual بهصورت ذاتی با یک گذرواژه کار میکند؛ بنابراین اشتراکگذاری یک بودجه بهمعنای اشتراکگذاری همان گذرواژه است.
سپس یک فایل بودجه ایجاد کنید. Actual میپرسد آیا میخواهید رمزنگاری سرتاسری را فعال کنید. پاسخ مثبت بدهید تا سرور فقط متن رمزشده را ذخیره کند؛ این گزینه برای دادههای مالی روی یک ماشین اجارهای مناسب است. این کار هزینهای واقعی دارد: گذرواژه رمزنگاری هرگز به سرور ارسال نمیشود. بنابراین اگر آن را گم کنید، فایل از بین میرود و امکان بازنشانی وجود ندارد. پیش از عبور از این صفحه، گذرواژه را یادداشت کنید.
موجودیهای اولیه را بر اساس ارقام فعلی بانک خود تنظیم کنید، نه با وارد کردن سوابق چندین سال گذشته. بودجهبندی پاکتی از پولی که اکنون در اختیار دارید به بعد پیش میرود؛ بنابراین نداشتن سوابق گذشته مشکلی ایجاد نمیکند.
ورود تراکنشها
در این بخش، صداقت از اشتیاق مهمتر است؛ زیرا نحوه وارد کردن دادهها دلیل اصلی کنار گذاشتن بودجهبندی self-hosted است.
ورود دستی، روش پایه است و همیشه کار میکند. در روش پاکتی، این کار را میتوان هدف اصلی دانست؛ زیرا وارد کردن یک خرید باعث میشود متوجه آن شوید.
وارد کردن فایل، بیشتر کار را انجام میدهد. Actual فایلهای CSV، QIF، OFX و QFX را میخواند و هر بانک دستکم یکی از این قالبها را صادر میکند. برای هر حساب، از صفحه حساب دادهها را وارد کنید، ستونها را یکبار نگاشت کنید تا Actual این چیدمان را برای حساب به خاطر بسپارد.
همگامسازی خودکار بانک وجود دارد، اما به یک سرویس شخص ثالث نیاز دارد؛ زیرا سرور بهتنهایی نمیتواند با بانکها ارتباط برقرار کند. Actual از SimpleFIN Bridge برای بانکهای آمریکای شمالی، Enable Banking برای اروپا، Akahu برای نیوزیلند و Pluggy.ai برای برزیل پشتیبانی میکند. GoCardless همچنان پشتیبانی میشود، اما حساب جدید نمیپذیرد. باید خودتان در سرویسدهنده ثبتنام کنید، اعتبارنامهها را ایجاد کنید و آنها را به سرور اضافه کنید. SimpleFIN Bridge تا July 2026، برای حداکثر 25 مؤسسه، سالانه 15 دلار آمریکا هزینه دارد و قیمتگذاری سرویسهای دیگر متفاوت است.
پیش از اتکا به این قابلیت، باید 2 محدودیت را بپذیرید. اعتبارنامههای API روی سرور قرار دارند و تحت رمزنگاری سرتاسری نیستند؛ زیرا سرور باید از آنها استفاده کند. همچنین Actual بهصورت دورهای درخواست ارسال نمیکند: همگامسازی یک دکمه است که آن را فشار میدهید، نه یک وظیفه پسزمینه.
پشتیبانگیری، چون فقط فایلها هستند
هر چیزی که برایتان مهم است، در /opt/actual/data قرار دارد. نیازی به مرحله export یا dump پایگاهداده برای script کردن آن نیست.
تنها دام، SQLite است. اگر account.sqlite را هنگام نوشتن server در آن کپی کنید، ممکن است یک transaction ناتمام ثبت شود و تا زمان تلاش برای restore متوجه این مشکل نشوید. container را برای چند ثانیهای که کپی طول میکشد متوقف کنید:
cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose startاین کار را با رویکرد توضیحدادهشده در پشتیبانگیری با restic روی VPS زمانبندی کنید. این رویکرد راهاندازی repository، نگهداری نسخهها و تمرین restore را پوشش میدهد. تمرین restore را اجرا کنید. پشتیبانی که هرگز آن را restore نکردهاید، فقط یک حدس است.
پشتیبانهای سمت client خود Actual موضوع جداگانهای هستند و دانستن آنها مفید است. مرورگر نسخههای اخیر فایل بودجه را نگه میدارد و از منوی فایل میتوان به آنها دسترسی داشت. بنابراین برای حالت «یک category را اشتباهی حذف کردم» نیازی به دستزدن به server نیست.
بهروزرسانی سرور
cd /opt/actual
docker compose pull
docker compose up --detachCompose کانتینر را از image جدید بازسازی میکند و همان volume را دوباره متصل میکند؛ بنابراین دادهها باقی میمانند. کلاینتها را نیز بهروزرسانی کنید. انتظار میرود نسخههای سرور و برنامه به یکدیگر نزدیک باشند. کلاینتی که بسیار قدیمیتر از سرور باشد، ممکن است بهدلیل ناسازگاری نسخه از همگامسازی خودداری کند. پیش از جهش به یک نسخه اصلی جدید، نسخه پشتیبان تهیه کنید؛ زیرا migrationها در نخستین راهاندازی اجرا میشوند و امکان downgrade وجود ندارد.
چه چیزهایی خراب میشوند و چه چیزی مشاهده خواهید کرد
برنامه بارگذاری میشود، اما همگامسازی هرگز کامل نمیشود. گزارش دسترسی nginx را برای 413 بررسی کنید. این مقدار client_max_body_size است که بیش از حد کم تنظیم شده است. وجود 502 بهجای آن یعنی nginx فعال است، اما container فعال نیست.
گزینههای رمزنگاری وجود ندارند یا برنامه موبایل URL را نمیپذیرد. صفحه در یک زمینه امن نیست. نوار نشانی http:// را همراه با یک IP address یا hostname نشان میدهد که localhost نیست. بهجای استفاده از راهکار موقت، certificate را اصلاح کنید.
پیامی نمایش داده میشود که فایل بودجه با این نسخه سازگار نیست. نسخههای client و server از یکدیگر فاصله گرفتهاند. هر دو را به یک release بهروزرسانی و سپس reload کنید.
container بهصورت حلقهای restart میشود. docker compose logs actual را بخوانید. خطای permission برای /data یعنی directory متصلشده برای user مربوط به container قابل نوشتن نیست. خطای address-in-use یعنی فرایند دیگری از قبل پورت 5006 را روی loopback در اختیار دارد.
بارگذاری نخست کند به نظر میرسد. هنگام باز کردن فایل بودجه، کل آن در browser دانلود میشود. ابتدا یک انتقال بزرگ انجام میشود و سپس خواندنهای محلی صورت میگیرد. این مشکل به sizing سرور مربوط نیست و افزودن RAM آن را تغییر نمیدهد.
FAQ
آیا Actual Budget برای کارکردن به HTTPS نیاز دارد؟
بله، در عمل. رمزنگاری سرتاسری Actual از Web Crypto API مرورگر استفاده میکند و مرورگرها این API را فقط در یک زمینه امن، یعنی https:// یا http://localhost، در دسترس قرار میدهند. هنگام استفاده از HTTP معمولی از یک ماشین دیگر، این قابلیتها در دسترس نیستند و برنامههای رسمی موبایل نیز نشانی سرور HTTP معمولی را نمیپذیرند. از گواهی Let's Encrypt روی یک نام میزبان واقعی استفاده کنید، یا اگر فقط از مرورگر دسکتاپ استفاده میکنید، با ACTUAL_HTTPS_KEY و ACTUAL_HTTPS_CERT یک گواهی خودامضا بسازید.
آیا Actual میتواند تراکنشهای بانکی من را بهصورت خودکار وارد کند؟
فقط از طریق یک سرویس شخص ثالث که خودتان در آن ثبتنام میکنید: SimpleFIN Bridge در آمریکای شمالی، Enable Banking در اروپا، Akahu در نیوزیلند، یا Pluggy.ai در برزیل. GoCardless پشتیبانی میشود، اما حساب جدید نمیپذیرد. اعتبارنامههای API این سرویسها روی سرور شما ذخیره میشوند و رمزنگاری سرتاسری از آنها محافظت نمیکند. همگامسازی نیز دستی است؛ بنابراین باید دکمهای را فشار دهید و هیچ فرایندی در پسزمینه بهصورت دورهای دادهها را بررسی نمیکند. برای وارد کردن CSV، QIF، OFX و QFX به هیچ شخص ثالثی نیاز ندارید.
دقیقاً از چه چیزی باید نسخه پشتیبان تهیه کنم؟
از شاخه دادهای که mount شده است؛ در این راهنما این شاخه /opt/actual/data است. این شاخه شامل server-files/account.sqlite برای اطلاعات ورود و نشستها، و user-files برای فایلهای بودجه است. پیش از کپیکردن، کانتینر را متوقف کنید، زیرا کپیکردن یک پایگاه داده زنده SQLite ممکن است نوشتن ناقص را ثبت کند. هیچ بخش دیگری از سرور وضعیت پایدار Actual را نگه نمیدارد.
اگر گذرواژه رمزنگاری را از دست بدهم چه اتفاقی میافتد؟
فایل قابل بازیابی نیست. گذرواژه هرگز به سرور ارسال نمیشود؛ این دقیقاً هدف رمزنگاری سرتاسری است. بنابراین امکان بازنشانی گذرواژه یا دریافت پشتیبانی برای بازیابی آن وجود ندارد. بلافاصله پس از ایجاد فایل، گذرواژه را در یک password manager ذخیره کنید و یک نسخه از آن را در مکانی نگه دارید که به همین سرور وابسته نباشد.
Actual به چه میزان منابع سرور نیاز دارد؟
به منابع بسیار کمی نیاز دارد. کانتینر فایلها و داراییهای ایستا را ارائه میکند و محاسبات بودجه در مرورگر انجام میشوند. یک vCPU اشتراکی با 1 GB RAM آن را بدون مشکل اجرا میکند و شاخه داده برای بودجه یک خانوار با چند سال سابقه معمولاً در محدوده دهها مگابایت باقی میماند. مصرف دیسک بیشتر به نسخههای پشتیبان شما و کانتینرهای دیگر مربوط است، نه Actual.