Self-hosted web UI that syncs transactions from Monobank into an Actual Budget server.
The goal is “start it, open the UI, configure, sync” — no env-var juggling required for normal use.
The easiest way to run the app is using the pre-built Docker image from the GitHub Container Registry. (If you are using CasaOS, skip to the CasaOS section below).
docker composeCreate a docker-compose.yml file:
services:
mono-actual-sync:
image: ghcr.io/sviatcraft/mono-actual-sync:latest
container_name: mono-actual-sync
ports:
- "9191:9191"
environment:
PORT: "9191"
# Set your timezone so imported transaction dates match your local day boundary.
TZ: "Europe/Kyiv"
volumes:
# Standard Docker named volume
- mono_actual_sync_data:/app/backend/data
restart: unless-stopped
volumes:
mono_actual_sync_data:
Run the following command in the same directory:
docker compose up -d
Open http://localhost:9191 (or your server's IP if you changed the port mapping).
This app supports native CasaOS integration. The easiest way to install it is to import the custom configuration below, which automatically handles the app icon, web UI routing, and saves your configuration directly to your CasaOS AppData folder.
Installation Steps:
services:
mono-actual-sync:
image: ghcr.io/sviatcraft/mono-actual-sync:latest
container_name: mono-actual-sync
ports:
- "9191:9191"
environment:
PORT: "9191"
TZ: "Europe/Kyiv"
volumes:
# CasaOS-specific bind mount for easy file access in the Files app
- /DATA/AppData/mono-actual-sync:/app/backend/data
restart: unless-stopped
# --- CasaOS App Store Metadata ---
x-casaos:
architectures:
- amd64
- arm64
main: "mono-actual-sync"
description:
en_us: "Self-hosted web UI that syncs transactions from Monobank into an Actual Budget server. Set it up once, map your accounts, and easily keep your budget up to date with manual or hourly background syncs."
tagline:
en_us: "Bridge Monobank to Actual Budget"
developer: "sviatcraft"
author: "sviatcraft"
icon: "https://raw.githubusercontent.com/sviatcraft/mono-actual-sync/main/frontend/public/favicon.svg"
thumbnail: ""
title:
en_us: "Mono Actual Sync"
category: "Finance"
port_map: "9191"
index: "/"
tips:
before_install:
en_us: "You will need your Monobank Personal Token and an existing Actual Budget server (URL, Password, and Sync ID) to configure this app after installation."
Once the app is running, open the web UI:
Server URL, Server Password, and Budget Sync ID./api/*.0.0.0.0:9191, making the UI/API reachable from your LAN."127.0.0.1:9191:9191"./data folder (encrypted at rest)..encryption_key.AppData/mono-actual-sync.config.json and .encryption_key together — losing the key means the config can’t be decrypted.The Actual Server URL must be reachable from inside the container.
http://host.docker.internal:<port>http://192.168.1.X:5006).If you prefer to build the Docker image locally instead of using the pre-built registry image:
git clone [https://github.com/sviatcraft/mono-actual-sync.git](https://github.com/sviatcraft/mono-actual-sync.git)
cd mono-actual-sync
docker compose -f docker-compose.dev.yml up --build -d
(Requires creating a local docker-compose.dev.yml that uses build: . instead of the image key).
Prereqs: Node.js 18+ and npm.
1) Backend
cd backend
npm install
npm run dev
2) Frontend
cd frontend
npm install
npm run dev
Open the Vite dev server URL (usually http://localhost:5173). In dev, Vite proxies /api → http://localhost:9191 (see frontend/vite.config.js).
Endpoints (all under /api):
GET /api/config – returns decrypted config (or defaults)POST /api/config – saves config (encrypted) and (re)starts cron depending on useNodeCronPOST /api/fetch-accounts – verifies credentials, fetches account lists, persists accountCachePOST /api/sync – starts a sync using the saved configGET /api/sync-status – returns { isSyncing: boolean }Rate limiting:
429 with a waitTime).Server URL, Budget Sync ID, and Server Password. Remember to use your server's LAN IP if running on Linux/CasaOS.x-casaos metadata for true 1-click CasaOS App Store integration./api/* (e.g. APP_TOKEN / Basic Auth)