ติดตั้ง Actual Budget บน VPS ด้วย Docker Compose
คู่มือติดตั้ง Actual Budget บน VPS ด้วย Docker Compose พร้อมจัดการ data volume, HTTPS ที่เบราว์เซอร์ต้องใช้, สร้าง budget, นำเข้าธนาคาร และสำรองข้อมูล
สิ่งที่คุณกำลังสร้าง
Actual Budget เป็นแอปจัดทำงบประมาณแบบซองเงินที่โฮสต์เองได้ และมักเป็นตัวเลือกสำหรับผู้ที่กำลังมองหา YNAB ทางเลือกที่สามารถโฮสต์เองได้ เซิร์ฟเวอร์ประกอบด้วยคอนเทนเนอร์ 1 รายการ วอลุ่มข้อมูล 1 รายการ และชื่อ HTTPS 1 ชื่อ ทุกอย่างที่งบประมาณทั่วไปต้องใช้ทำงานได้อย่างราบรื่นบน VPS ขนาดเล็กที่สุดที่คุณเช่าได้ เนื่องจากเซิร์ฟเวอร์ทำหน้าที่จัดเก็บไฟล์และซิงค์ข้อมูลเป็นหลัก
ควรทำความเข้าใจสถาปัตยกรรมก่อนเริ่มพิมพ์คำสั่ง งบประมาณเป็นฐานข้อมูล SQLite ที่อยู่ในเบราว์เซอร์และในแอปมือถือแต่ละเครื่อง เซิร์ฟเวอร์ที่คุณกำลังจะติดตั้งเป็นปลายทางสำหรับซิงค์ข้อมูล โดยเก็บรายการบัญชี ไฟล์งบประมาณ และบันทึกการเปลี่ยนแปลงที่ทำให้โทรศัพท์และแล็ปท็อปมีข้อมูลตรงกัน นี่คือเหตุผลที่แอปยังทำงานได้เมื่อเซิร์ฟเวอร์หยุดทำงาน และเป็นเหตุผลที่การสูญเสียเซิร์ฟเวอร์ไม่ทำให้งบประมาณสูญหาย ตราบใดที่ยังมีไคลเอ็นต์อย่างน้อย 1 เครื่องเก็บสำเนาไว้
เหตุผลที่เซิร์ฟเวอร์ต้องใช้ HTTPS
Actual ต้องการ HTTPS และไม่ใช่เพียงขั้นตอนตามธรรมเนียม เบราว์เซอร์จะแสดง Web Crypto API ซึ่งเป็นอินเทอร์เฟซที่ Actual ใช้สำหรับการเข้ารหัสแบบต้นทางถึงปลายทาง เฉพาะในสิ่งที่ข้อกำหนดเรียกว่า secure context เท่านั้น secure context คือ https:// หรือ http://localhost โหลดแอปจาก http://203.0.113.10:5006 ในเบราว์เซอร์บนเครื่องอื่น แล้วฟีเจอร์เหล่านี้จะไม่พร้อมใช้งาน เนื่องจากเบราว์เซอร์ไม่ได้ส่งฟีเจอร์ดังกล่าวให้หน้าเว็บเลย ส่วน mobile builds อย่างเป็นทางการก็ปฏิเสธ URL ของเซิร์ฟเวอร์แบบ plain http:// เช่นกัน
ดังนั้นจึงมีการตั้งค่าที่ใช้งานได้ 2 แบบ ให้ใส่ certificate จริงบนชื่อจริงไว้หน้า container ซึ่งเป็นวิธีที่คู่มือนี้ใช้ หรือให้เซิร์ฟเวอร์ใช้ self-signed certificate ด้วย ACTUAL_HTTPS_KEY และ ACTUAL_HTTPS_CERT ตามที่โครงการระบุไว้ แล้วต้องยอมรับคำเตือนของเบราว์เซอร์บนอุปกรณ์ทุกเครื่อง certificate ฟรีจาก 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ในไฟล์นี้มีรายละเอียดสำคัญ 3 รายการ
อิมเมจคือ 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 ดังนั้นแอปจะเปิดให้เข้าถึงจากอินเทอร์เน็ต แม้ไฟร์วอลล์จะตั้งค่าให้ปฏิเสธทั้งหมดก็ตาม อธิบายกรณีนี้ไว้ใน เหตุผลที่พอร์ตที่ Docker เผยแพร่ข้าม ufw การ bind กับ loopback หมายความว่ามีเพียง reverse proxy บนเครื่องเดียวกันเท่านั้นที่เข้าถึงได้
เริ่มการทำงาน:
cd /opt/actual
docker compose up --detach
docker compose logs -f actualล็อกจะนิ่งเมื่อเซิร์ฟเวอร์รายงานว่ากำลัง listen อยู่ที่พอร์ต 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 จะแสดงว่าคอนเทนเนอร์หยุดทำงานแล้ว สาเหตุที่พบบ่อยคือปัญหาสิทธิ์ใน volume ที่เมานต์ไว้ ซึ่งจะแสดงเป็นบรรทัด 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 กำหนดค่าเริ่มต้นของ request body ไว้ที่ 1 MB ดังนั้นเมื่อไฟล์มีขนาดเกินค่าดังกล่าว การซิงค์จะล้มเหลว โดยมี 413 Request Entity Too Large ใน nginx access log ขณะที่แอปพลิเคชันแสดงเพียงข้อผิดพลาดการซิงค์ทั่วไป เซิร์ฟเวอร์มีขีดจำกัดแยกต่างหาก ได้แก่ 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 ที่โฮสต์เอง ไม่จำเป็นต้องสร้างบัญชีผู้ใช้ Actual ออกแบบให้เซิร์ฟเวอร์ใช้รหัสผ่านเดียว ดังนั้นการแชร์งบประมาณจึงหมายถึงการแชร์รหัสผ่านนั้น
จากนั้นสร้างไฟล์งบประมาณ Actual จะถามว่าต้องการเปิดใช้การเข้ารหัสแบบต้นทางถึงปลายทางหรือไม่ ให้ตอบตกลง แล้วเซิร์ฟเวอร์จะจัดเก็บเฉพาะ ciphertext ซึ่งเป็นตัวเลือกที่เหมาะสมสำหรับข้อมูลทางการเงินบนเครื่องที่เช่า ข้อแลกเปลี่ยนนี้มีความสำคัญ: รหัสผ่านสำหรับการเข้ารหัสจะไม่ถูกส่งไปยังเซิร์ฟเวอร์ ดังนั้นหากคุณทำหาย ไฟล์จะไม่สามารถกู้คืนได้ และไม่มีวิธีรีเซ็ต ให้จดรหัสผ่านไว้ก่อนคลิกผ่านหน้าจอนั้น
กำหนดยอดคงเหลือเริ่มต้นตามยอดปัจจุบันจากธนาคาร แทนการนำเข้าประวัติย้อนหลังหลายปี การจัดทำงบประมาณแบบซองจะเริ่มคำนวณต่อจากเงินที่คุณมีอยู่ในขณะนี้ ดังนั้นการไม่มีประวัติเดิมจึงไม่ทำให้คุณสูญเสียสิ่งใด
การนำเข้าธุรกรรม
ส่วนนี้ต้องให้ข้อมูลตามจริงมากกว่าการนำเสนอเชิงบวก เพราะความสามารถด้านการนำเข้าข้อมูลเป็นเหตุผลหลักที่ทำให้ผู้ใช้เลิกใช้ซอฟต์แวร์จัดทำงบประมาณแบบ self-hosted
การบันทึกด้วยตนเองเป็นวิธีพื้นฐานและใช้งานได้เสมอ สำหรับวิธีจัดสรรงบประมาณแบบซอง วิธีนี้อาจถือเป็นหัวใจสำคัญ เพราะการพิมพ์รายการซื้อทำให้คุณตระหนักถึงรายการนั้น
การนำเข้าจากไฟล์ช่วยจัดการรายการจำนวนมาก Actual อ่านไฟล์ CSV, QIF, OFX และ QFX ได้ และธนาคารทุกแห่งมีรูปแบบเหล่านี้อย่างน้อย 1 รูปแบบ ให้ทำการนำเข้าแยกตามบัญชีจากหน้าบัญชี จับคู่คอลัมน์เพียงครั้งเดียว แล้ว Actual จะจดจำรูปแบบดังกล่าวไว้สำหรับบัญชีนั้น
มีการซิงค์กับธนาคารโดยอัตโนมัติ แต่ต้องใช้บริการจากบุคคลที่สาม เพราะเซิร์ฟเวอร์ไม่สามารถเชื่อมต่อกับธนาคารได้โดยลำพัง Actual รองรับ SimpleFIN Bridge สำหรับธนาคารในอเมริกาเหนือ, Enable Banking สำหรับยุโรป, Akahu สำหรับนิวซีแลนด์ และ Pluggy.ai สำหรับบราซิล GoCardless ยังรองรับอยู่ แต่ไม่รับบัญชีใหม่แล้ว คุณต้องสมัครใช้บริการกับผู้ให้บริการด้วยตนเอง สร้างข้อมูลรับรอง แล้วเพิ่มข้อมูลดังกล่าวลงในเซิร์ฟเวอร์ SimpleFIN Bridge คิดค่าบริการ 15 ดอลลาร์สหรัฐต่อปีสำหรับสถาบันการเงินสูงสุด 25 แห่ง ณ เดือน July 2026 ส่วนผู้ให้บริการรายอื่นมีอัตราค่าบริการแตกต่างกัน
มีข้อจำกัด 2 ประการที่ควรยอมรับก่อนพึ่งพาความสามารถนี้ ข้อมูลรับรอง API จะอยู่บนเซิร์ฟเวอร์และไม่อยู่ภายใต้การเข้ารหัสแบบ end-to-end เนื่องจากเซิร์ฟเวอร์ต้องใช้ข้อมูลดังกล่าว และ 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 ซึ่งครอบคลุมการตั้งค่า repository การเก็บรักษาข้อมูล และการฝึกซ้อมกู้คืน ให้ดำเนินการฝึกซ้อมกู้คืนด้วย การสำรองข้อมูลที่ไม่เคยทดสอบกู้คืนเป็นเพียงการคาดเดา
การสำรองข้อมูลฝั่งไคลเอ็นต์ของ Actual เป็นอีกส่วนหนึ่งที่ควรทราบ เบราว์เซอร์จะเก็บสำเนาล่าสุดของไฟล์งบประมาณไว้ ซึ่งเข้าถึงได้จากเมนูไฟล์ วิธีนี้ช่วยแก้กรณี “ลบหมวดหมู่โดยไม่ตั้งใจ” ได้โดยไม่ต้องดำเนินการใดกับเซิร์ฟเวอร์เลย
การอัปเดตเซิร์ฟเวอร์
cd /opt/actual
docker compose pull
docker compose up --detachCompose จะสร้างคอนเทนเนอร์ขึ้นใหม่จากอิมเมจใหม่และเชื่อมต่อ volume เดิมอีกครั้ง ดังนั้นข้อมูลจึงยังคงอยู่ ให้อัปเดตไคลเอ็นต์ด้วย เวอร์ชันของเซิร์ฟเวอร์และแอปควรใกล้เคียงกัน และไคลเอ็นต์ที่เก่ากว่าเซิร์ฟเวอร์มากอาจปฏิเสธการซิงค์ พร้อมแสดงข้อความแจ้งว่าเวอร์ชันไม่ตรงกัน ให้สำรองข้อมูลก่อนข้ามไปใช้เวอร์ชันหลักใหม่ เนื่องจากระบบจะเรียกใช้การย้ายข้อมูลเมื่อเริ่มต้นครั้งแรก และไม่มีเส้นทางสำหรับดาวน์เกรด
สิ่งที่ขัดข้องและสิ่งที่จะพบ
แอปโหลดได้ แต่การซิงค์ไม่เสร็จสิ้น ตรวจสอบ access log ของ nginx สำหรับ 413 นั่นหมายความว่า client_max_body_size ถูกตั้งค่าไว้ต่ำเกินไป ส่วน 502 หมายความว่า nginx ทำงานอยู่ แต่ container ไม่ได้ทำงาน
ไม่มีตัวเลือกการเข้ารหัส หรือแอปบนอุปกรณ์เคลื่อนที่ปฏิเสธ URL หน้าเว็บไม่ได้อยู่ใน secure context แถบที่อยู่จะแสดง http:// พร้อมด้วย IP address หรือ hostname ที่ไม่ใช่ localhost ให้แก้ไข certificate แทนการใช้วิธีหลีกเลี่ยงปัญหา
มีข้อความว่าไฟล์งบประมาณไม่เข้ากันได้กับเวอร์ชันนี้ เวอร์ชันของ client และ server ไม่ตรงกันแล้ว ให้อัปเดตทั้งสองส่วนเป็น release เดียวกัน แล้วโหลดใหม่
container รีสตาร์ตวนซ้ำ อ่าน docker compose logs actual ข้อผิดพลาดด้านสิทธิ์ใน /data หมายความว่า directory ที่ mount ไว้ไม่สามารถเขียนได้โดย user ของ container ข้อผิดพลาด address-in-use หมายความว่ามีสิ่งอื่นใช้ 5006 บน loopback อยู่แล้ว
การโหลดครั้งแรกรู้สึกช้า ไฟล์งบประมาณทั้งหมดจะถูกดาวน์โหลดไปยัง browser เมื่อคุณเปิดไฟล์ การทำงานจึงเป็นการถ่ายโอนข้อมูลขนาดใหญ่หนึ่งครั้ง แล้วจึงอ่านข้อมูลภายในเครื่อง ปัญหานี้ไม่ได้เกิดจากการจัดสรรทรัพยากรของ server และการเพิ่ม RAM จะไม่ช่วยให้เร็วขึ้น
FAQ
Actual Budget ต้องใช้ HTTPS จึงจะทำงานได้หรือไม่?
ในทางปฏิบัติ จำเป็นต้องใช้ Actual ใช้ Web Crypto API ของเบราว์เซอร์สำหรับการเข้ารหัสแบบ end-to-end และเบราว์เซอร์จะแสดง API นี้เฉพาะใน secure context ซึ่งหมายถึง https:// หรือ http://localhost หากเข้าถึงเซิร์ฟเวอร์จากเครื่องอื่นผ่าน HTTP ธรรมดา ฟังก์ชันเหล่านี้จะไม่พร้อมใช้งาน และแอปมือถืออย่างเป็นทางการจะปฏิเสธ URL ของเซิร์ฟเวอร์ที่ใช้ HTTP ธรรมดา ใช้ใบรับรอง Let's Encrypt บน hostname จริง หรือใช้ใบรับรองแบบ self-signed ร่วมกับ ACTUAL_HTTPS_KEY และ ACTUAL_HTTPS_CERT หากคุณใช้งานผ่านเบราว์เซอร์เดสก์ท็อปเท่านั้น
Actual สามารถนำเข้าธุรกรรมธนาคารโดยอัตโนมัติได้หรือไม่?
ได้เฉพาะผ่านบริการของบุคคลที่สามที่คุณสมัครใช้งานด้วยตนเอง ได้แก่ SimpleFIN Bridge ในอเมริกาเหนือ, Enable Banking ในยุโรป, Akahu ในนิวซีแลนด์ หรือ Pluggy.ai ในบราซิล GoCardless รองรับการใช้งาน แต่ไม่เปิดรับบัญชีใหม่แล้ว ข้อมูลรับรอง API เหล่านี้จัดเก็บอยู่บนเซิร์ฟเวอร์ของคุณ และไม่อยู่ภายใต้การเข้ารหัสแบบ end-to-end การซิงค์ก็ทำด้วยตนเองเช่นกัน ดังนั้นคุณต้องกดปุ่ม และระบบจะไม่ดึงข้อมูลอยู่เบื้องหลัง CSV, QIF, OFX และ QFX สามารถนำเข้าได้โดยไม่ต้องใช้บริการของบุคคลที่สาม
ต้องสำรองข้อมูลส่วนใดบ้าง?
ไดเรกทอรีข้อมูลที่ mount อยู่ ซึ่งในคู่มือนี้คือ /opt/actual/data ภายในมี server-files/account.sqlite ซึ่งเก็บข้อมูลการเข้าสู่ระบบและเซสชัน และมี user-files ซึ่งเก็บไฟล์งบประมาณ ให้หยุด container ก่อนคัดลอก เนื่องจากการคัดลอกฐานข้อมูล SQLite ที่กำลังทำงานอยู่อาจได้ข้อมูลที่เขียนไม่ครบ การเก็บสถานะของเซิร์ฟเวอร์ไม่มีส่วนอื่นใดนอกเหนือจากนี้
หากสูญเสียรหัสผ่านการเข้ารหัส จะเกิดอะไรขึ้น?
จะไม่สามารถกู้คืนไฟล์ได้ รหัสผ่านจะไม่ถูกส่งไปยังเซิร์ฟเวอร์ ซึ่งเป็นหลักการสำคัญของการเข้ารหัสแบบ end-to-end ดังนั้นจึงไม่มีวิธีรีเซ็ตและไม่มีช่องทางขอความช่วยเหลือ บันทึกรหัสผ่านไว้ใน password manager ทันทีที่สร้างไฟล์ และเก็บสำเนาไว้ในสถานที่ที่ไม่ต้องพึ่งพาเซิร์ฟเวอร์เครื่องเดียวกันนี้
Actual Budget ต้องใช้ทรัพยากรเซิร์ฟเวอร์มากเพียงใด?
ใช้น้อยมาก container ให้บริการไฟล์ static และไฟล์ต่าง ๆ ส่วนการคำนวณงบประมาณเกิดขึ้นในเบราว์เซอร์ shared vCPU จำนวน 1 หน่วย พร้อม RAM ขนาด 1 GB สามารถใช้งานได้โดยไม่มีปัญหา และไดเรกทอรีข้อมูลสำหรับงบประมาณของครัวเรือนที่มีประวัติหลายปีจะมีขนาดอยู่ในระดับหลายสิบ megabytes พื้นที่ดิสก์ที่ถูกใช้มากมาจากข้อมูลสำรองและ container อื่น ๆ ของคุณ ไม่ใช่จาก Actual