آموزش نصب Actual Budget روی VPS با Docker Compose
نصب Actual Budget روی سرور شخصی با Docker Compose. این راهنما نحوه تنظیم volume داده، پیکربندی HTTPS برای Web Crypto API و مدیریت فایلهای بودجه را برای همگامسازی امن آموزش میدهد.
آنچه میسازید
Actual Budget یک برنامه بودجهبندی مبتنی بر سیستم پاکت (envelope budgeting) است که میتوانید آن را روی سرور شخصی خود میزبانی کنید. این برنامه معمولترین جایگزین برای YNAB است که کاربران برای میزبانی شخصی به سراغ آن میروند. سرور این برنامه شامل یک container، یک volume داده و یک نام HTTPS است. تمام نیازهای یک بودجهبندی معمولی بهراحتی روی کوچکترین VPS قابل اجاره اجرا میشود، زیرا وظیفه اصلی سرور، ذخیرهسازی فایلها و همگامسازی آنهاست.
پیش از آنکه هر دستوری را تایپ کنید، درک معماری آن اهمیت دارد. خودِ بودجه، یک پایگاه داده SQLite است که درون مرورگر و هر یک از برنامههای موبایل شما قرار دارد. سروری که قصد نصب آن را دارید، یک نقطه پایانی (endpoint) برای همگامسازی است: این سرور لیست حسابها، فایلهای بودجه و لاگ تغییراتی را نگه میدارد که باعث میشود گوشی و لپتاپ شما با هم هماهنگ باشند. به همین دلیل است که برنامه حتی در زمان قطع بودن سرور همچنان کار میکند و به همین دلیل است که از دست رفتن سرور به معنای از دست رفتن بودجه شما نیست، تا زمانی که حداقل یک کلاینت، نسخهای از آن را در اختیار داشته باشد.
چرا سرور به HTTPS نیاز دارد
Actual برای اجرا به قابلیتی در مرورگر به نام SharedArrayBuffer نیاز دارد و این یک تشریفات اداری نیست. مرورگرها تنها زمانی این قابلیت را فعال میکنند که صفحه در وضعیتی که مشخصات فنی آن را «محیط امن» (secure context) مینامد، بهصورت cross-origin isolated باشد. محیط امن یعنی https:// یا http://localhost. اگر برنامه را از طریق http://203.0.113.10:5006 در مرورگر یک دستگاه دیگر بارگذاری کنید، مرورگر هرگز این قابلیت را در اختیار صفحه قرار نمیدهد و در نتیجه بودجه بارگذاری نمیشود. گوشیها نیز از این قاعده مستثنی نیستند. طبق FAQ برنامه Actual، یک نسخه رسمی iOS در مرحله تست بتا قرار دارد که به سرور شخصی شما متصل میشود و هنوز نسخه رسمی اندروید وجود ندارد؛ بنابراین در اکثر گوشیها از همان وباپلیکیشن استفاده میکنید که بهعنوان یک Progressive Web App روی صفحه اصلی نصب میشود و همان قانون محیط امن در مورد آن صدق میکند.
بنابراین دو پیکربندی عملی وجود دارد. یا یک گواهی معتبر روی یک نام دامنه واقعی در مقابل کانتینر قرار دهید که این راهنما همین کار را انجام میدهد، یا به سرور یک گواهی خود-امضا (self-signed) با 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 (حاوی فایلهای بودجه) است. حتماً این مسیر را Mount کنید، در غیر این صورت docker compose pull بودجه شما را حذف میکند. ACTUAL_DATA_DIR میتواند آن را جابهجا کند، اما مقدار پیشفرض مناسب است.
پورت فقط روی 127.0.0.1 منتشر میشود. یک 5006:5006 ساده، پورت را روی تمام اینترفیسها منتشر میکند و Docker قوانین خود را پیش از ufw اعمال میکند؛ بنابراین برنامه حتی با وجود فایروال deny-all، در دسترس اینترنت قرار میگیرد. این غافلگیری در چرا پورتهای منتشرشده توسط 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 قرار دارد، علت معمول آن مشکل مجوز (permission) در volume مونتشده است که به صورت یک خط EACCES در لاگها قابل مشاهده است.
قرار دادن گواهی و نام دامنه واقعی در مقابل سرویس
یک رکورد A را به سمت VPS خود، budget.example.com، تنظیم کنید و منتظر بمانید تا DNS آن را شناسایی کند. سپس 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 برای بدنه درخواست (request body) برابر با 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 را در مرورگر باز کنید. صفحه اول از شما میخواهد یک رمز عبور برای سرور تعیین کنید. این رمز عبور واحد، از کل سرور محافظت میکند؛ بنابراین یک رمز عبور طولانی و تصادفی ایجاد کنید و آن را در جایی امن نگهداری کنید، مانند مدیریت رمز عبور Vaultwarden که خودتان میزبانی میکنید. بهصورت پیشفرض، حساب کاربری برای ایجاد وجود ندارد: سرور تنها یک رمز عبور دارد، بنابراین اشتراکگذاری بودجه به معنای اشتراکگذاری همان رمز عبور است. ورودهای مجزا تنها در صورتی ممکن است که یک ارائهدهنده OpenID متصل کنید که Actual برای تنظیمات چندکاربره از آن پشتیبانی میکند.
سپس یک فایل بودجه ایجاد کنید. Actual از شما میپرسد که آیا میخواهید رمزنگاری سرتاسری (end-to-end encryption) را فعال کنید یا خیر. پاسخ مثبت بدهید؛ در این حالت سرور فقط متن رمزنگاریشده (ciphertext) را ذخیره میکند که برای دادههای مالی روی یک ماشین اجارهای، بهترین گزینه است. هزینه این کار واقعی است: رمز عبور رمزنگاری هرگز به سرور ارسال نمیشود، بنابراین اگر آن را گم کنید، فایل از دست میرود و هیچ راهی برای بازیابی آن وجود ندارد. پیش از عبور از این صفحه، آن را یادداشت کنید.
موجودیهای اولیه خود را بر اساس ارقام فعلی بانکتان تنظیم کنید، نه با وارد کردن تاریخچهای از چندین سال. بودجهبندی پاکتی (Envelope budgeting) از پولی که اکنون در اختیار دارید به سمت آینده حرکت میکند، بنابراین نداشتن تاریخچه هیچ خللی در کار شما ایجاد نمیکند.
وارد کردن تراکنشها
در این بخش، صداقت اهمیت بیشتری نسبت به اشتیاق دارد، زیرا نحوه وارد کردن دادهها اصلیترین دلیلی است که باعث میشود کاربران از بودجهبندی self-hosted منصرف شوند.
ثبت دستی، روش پایه است و همیشه کار میکند. برای روش پاکتبندی (envelope method)، میتوان گفت که این موضوع تا حدی هدف اصلی است، زیرا تایپ کردن یک خرید همان چیزی است که باعث میشود متوجه آن شوید.
وارد کردن فایل (File import) حجم اصلی کار را انجام میدهد. Actual از فرمتهای CSV، QIF، OFX، QFX و CAMT پشتیبانی میکند و هر بانکی حداقل یکی از اینها را خروجی میدهد. وارد کردن را برای هر حساب از صفحه همان حساب انجام دهید و در صورتی که فایل CSV است، ستونها را نگاشت (map) کنید.
همگامسازی خودکار بانکی وجود دارد و به یک سرویس شخص ثالث نیاز دارد، زیرا سرور نمیتواند بهتنهایی با بانکها ارتباط برقرار کند. Actual از SimpleFIN Bridge برای بانکهای آمریکای شمالی، Enable Banking برای اروپا، Akahu برای نیوزیلند و Pluggy.ai برای برزیل پشتیبانی میکند. GoCardless همچنان پشتیبانی میشود اما حساب جدید نمیپذیرد. شما باید شخصاً در سایت ارائهدهنده ثبتنام کنید، اعتبارنامه (credentials) بسازید و آنها را به سرور اضافه کنید. تا اکتبر 2026، هزینه SimpleFIN Bridge برای حداکثر 25 موسسه، 15 دلار آمریکا در سال به اضافه مالیات است و سایر سرویسها قیمتگذاری متفاوتی دارند.
پیش از تکیه بر این قابلیت، دو محدودیت را بپذیرید. اعتبارنامههای API روی سرور قرار میگیرند و تحت پوشش رمزنگاری سرتاسری (end-to-end encryption) نیستند، زیرا سرور باید از آنها استفاده کند. همچنین تا زمانی که Actual بسته است، هیچچیز همگامسازی نمیشود: همگامسازی خودکار بهصورت پیشفرض خاموش است و پس از روشن کردن، فقط زمانی اجرا میشود که برنامه باز باشد؛ بنابراین سرور هرگز بهتنهایی از بانکهای شما استعلام نمیگیرد.
پشتیبانگیری، چون همهچیز فقط فایل است
تمام دادههایی که برایتان اهمیت دارد در مسیر /opt/actual/data قرار گرفته است. هیچ مرحلهٔ خروجی گرفتن (export) یا اسکریپت دامپ پایگاهدادهای وجود ندارد.
تنها نکتهٔ مهم، SQLite است. کپی کردن account.sqlite در حالی که سرور در حال نوشتن روی آن است، ممکن است منجر به ثبت یک تراکنش ناقص شود و شما تا زمانی که قصد بازیابی نداشته باشید، متوجه آن نخواهید شد. برای چند ثانیهای که کپی انجام میشود، 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)، سیاست نگهداری و تمرین بازیابی است. تمرین بازیابی را حتماً انجام دهید. پشتیبانگیریای که هرگز بازیابی نشده، صرفاً یک حدس و گمان است.
پشتیبانگیریهای خودکار خودِ Actual موضوعی جداگانه است و دانستن آن مفید است، اما این قابلیت فقط در نسخهٔ دسکتاپ وجود دارد. این برنامه تا 10 نسخهٔ اخیر از فایل بودجه را نگه میدارد که از طریق مسیر File > Load Backup در دسترس است. این قابلیت برای مواردی مثل «حذف اشتباهی یک دستهبندی» بدون نیاز به دسترسی به سرور کاربرد دارد. نسخهٔ وب در مرورگر چنین نسخههایی را نگه نمیدارد؛ بنابراین اگر همهٔ اعضای خانه از مرورگر استفاده میکنند، پشتیبانگیری سرور که در بالا ذکر شد، تنها راه نجات شماست.
بهروزرسانی سرور
cd /opt/actual
docker compose pull
docker compose up --detachدستور Compose کانتینر را از روی ایمیج جدید بازسازی کرده و همان volume قبلی را به آن متصل میکند، بنابراین دادهها حفظ میشوند. کلاینتها را نیز بهروزرسانی کنید. کلاینتی که قدیمیتر از دادههای دریافتی باشد، اعمال تغییرات را متوقف کرده و پیام Update Required را نمایش میدهد. پیش از جهش به نسخه اصلی (major version)، حتماً نسخه پشتیبان تهیه کنید؛ زیرا عملیات مهاجرت (migration) در اولین اجرا انجام میشود و مسیر بازگشت به نسخه قبلی وجود ندارد. Actual نسبت به تگ شناور latest منعطف است، زیرا وضعیت آن شامل مجموعهای از فایلها در یک دایرکتوری است؛ اما برنامهای که از دیتابیس واقعی استفاده میکند چنین نیست. در میزبانی شخصی Chatwoot، مراحل استفاده از تگهای ثابت (pinned tags) و تهیه dump پیش از ارتقا که یک عادت ضروری است، توضیح داده شده است.
چه چیزی دچار اختلال میشود و چه چیزی مشاهده خواهید کرد
برنامه بارگذاری میشود اما همگامسازی (sync) هرگز به پایان نمیرسد. لاگ دسترسی nginx را برای 413 بررسی کنید. این به معنای آن است که client_max_body_size روی مقدار بسیار کمی تنظیم شده است. در مقابل، 502 نشان میدهد که nginx پاسخ قابلاستفادهای از upstream دریافت نکرده است. در بیشتر موارد، دلیل این امر متوقف بودن کانتینر یا عدم گوش دادن آن روی پورت 5006 است؛ بنابراین پیش از هر اقدام دیگری، docker compose ps --all و لاگ کانتینر را بررسی کنید.
برنامه روی دستگاه دیگری بارگذاری نمیشود یا گزارش میدهد که SharedArrayBuffer موجود نیست. صفحه در یک بستر امن (secure context) قرار ندارد. نوار آدرس، http:// را با یک آدرس IP یا نام میزبانی نشان میدهد که localhost نیست. به جای دور زدن مشکل، گواهی (certificate) را اصلاح کنید.
پیامی مبنی بر اینکه فایل بودجه با این نسخه سازگار نیست. فایل در نسخهای از Actual باز شده است که جدیدتر از کلاینتی است که اکنون استفاده میکنید. کلاینت را حداقل به همان نسخه بهروزرسانی کنید یا یک نسخه پشتیبان که پیش از دسترسی نسخه جدید به فایل تهیه شده است را بازیابی کرده و سپس صفحه را مجدداً بارگذاری کنید.
کانتینر در یک حلقه (loop) مدام راهاندازی مجدد میشود. docker compose logs actual را بخوانید. خطای مجوز (permission error) در /data به این معناست که دایرکتوری mount شده توسط کاربر کانتینر قابل نوشتن نیست. خطای address-in-use نشان میدهد که سرویس دیگری در حال حاضر پورت 5006 را روی loopback اشغال کرده است.
بارگذاری اولیه کند به نظر میرسد. هنگام باز کردن فایل بودجه، کل فایل در مرورگر دانلود میشود. این یک انتقال داده بزرگ است و پس از آن، خواندن اطلاعات بهصورت محلی انجام میشود. این مشکل مربوط به ابعاد سرور نیست و افزودن RAM تغییری در آن ایجاد نخواهد کرد.
FAQ
آیا Actual Budget برای کار کردن به HTTPS نیاز دارد؟
بله، در عمل همینطور است. Actual برای اجرا به قابلیت SharedArrayBuffer مرورگر نیاز دارد و مرورگرها این قابلیت را فقط در بستر امن، یعنی https:// یا http://localhost فعال میکنند. اگر از طریق HTTP ساده و از دستگاه دیگری به آن متصل شوید، برنامه بارگذاری نخواهد شد. یک نسخه رسمی iOS در مرحله تست بتا قرار دارد و هنوز نسخه رسمی برای Android وجود ندارد، بنابراین اکثر گوشیها از همان وباپلیکیشن استفاده میکنند و با همین محدودیت مواجه میشوند. (FAQ) از یک گواهی Let’s Encrypt روی یک نام دامنه واقعی استفاده کنید، یا اگر میتوانید با هشدار گواهی در هر دستگاه کنار بیایید، از یک گواهی self-signed با ACTUAL_HTTPS_KEY و ACTUAL_HTTPS_CERT استفاده کنید.
آیا Actual میتواند تراکنشهای بانکی من را بهطور خودکار وارد کند؟
فقط از طریق سرویسهای شخص ثالثی که خودتان در آنها ثبتنام میکنید: SimpleFIN Bridge در آمریکای شمالی، Enable Banking در اروپا، Akahu در نیوزیلند، یا Pluggy.ai در برزیل. GoCardless پشتیبانی میشود اما حساب جدید نمیپذیرد. این اعتبارنامههای API روی سرور شما ذخیره میشوند و شامل رمزنگاری سرتاسری (end-to-end encryption) نمیشوند. همگامسازی خودکار بهصورت پیشفرض غیرفعال است و پس از فعالسازی، فقط زمانی اجرا میشود که Actual باز باشد؛ بنابراین هیچ فرآیندی در پسزمینه سرور اجرا نمیشود. برای وارد کردن فایلهای CSV، QIF، OFX، QFX و CAMT نیازی به هیچ شخص ثالثی نیست.
دقیقاً از چه چیزی باید نسخه پشتیبان تهیه کنم؟
از دایرکتوری دادههای mount شده که در این راهنما /opt/actual/data نامیده شده است. این دایرکتوری شامل server-files/account.sqlite برای ورودها و نشستها، و user-files برای فایلهای بودجه است. پیش از کپی کردن، container را متوقف کنید، زیرا کپی کردن یک دیتابیس SQLite در حال اجرا ممکن است منجر به ثبت یک نسخه ناقص (partial write) شود. هیچ جای دیگری روی سرور وضعیت (state) برنامه را نگه نمیدارد.
اگر رمز عبور رمزنگاری را گم کنم چه میشود؟
فایل قابل بازیابی نیست. رمز عبور هرگز به سرور ارسال نمیشود و این دقیقاً هدف اصلی رمزنگاری سرتاسری است، بنابراین امکان بازنشانی (reset) یا مسیر پشتیبانی برای آن وجود ندارد. به محض ایجاد فایل، آن را در یک مدیریتکننده رمز عبور (password manager) ذخیره کنید و یک نسخه از آن را در جایی نگه دارید که به همین سرور وابسته نباشد.
Actual Budget به چه میزان منابع سرور نیاز دارد؟
بسیار کم. این container فقط داراییهای استاتیک و فایلها را ارائه میدهد و محاسبات بودجه در مرورگر انجام میشود. یک vCPU اشتراکی با 1 GB رم بدون مشکل آن را اجرا میکند و دایرکتوری داده برای بودجه یک خانواده با چندین سال سابقه، تنها چند ده مگابایت فضا اشغال میکند. فشار روی دیسک ناشی از نسخههای پشتیبان و سایر containerهای شماست، نه Actual. اگر در حال انتخاب سروری هستید که باید برنامههای سنگینتری را در کنار آن اجرا کند، معمولاً سرور عکس تعیینکننده حداقل منابع است؛ بنابراین پیش از انتخاب پلن، میزان رم مورد نیاز واقعی برای PhotoPrism و Immich را بررسی کنید. همین منطق برای استکهای رسانهای نیز صدق میکند: transcoding تعیینکننده پلن است، در حالی که یک رابط کاربری مرورگر مانند Halcyon که کتابخانه Jellyfin را به یک فروشگاه ویدیوی دهه نودی قابل گشتوگذار تبدیل میکند، تقریباً به اندازه Actual منابع مصرف میکند.