Files
mikrotik-backup/README.md
T
2026-10-28 09:16:54 +01:00

5.5 KiB

MikroTik Backup System

Version 0.0.1 is the initial Rust rewrite of the previous Python/TypeScript application. It is a single self-hosted service with an embedded web UI, SQLite storage, RouterOS/SwOS backup workflows, scheduling, retention, notifications, and local OpenAPI documentation.

Quick start

Docker Compose

docker compose build app
docker compose up -d

The default published address is 127.0.0.1:5581. Persistent application data is stored in the data volume.

The image uses rust:1.99.1-alpine3.24 for the build stage and alpine:3.24 for runtime. The service runs as UID/GID 10001 with a read-only root filesystem.

Native build

Requires Rust 1.99.1 or compatible toolchain and the native dependencies needed by the crates in Cargo.lock.

cargo build --release --locked
./target/release/mikrotik-backup serve

Or use the launcher:

./start.sh

First account

Registration is disabled by default. Set DEFAULT_ADMIN_PASSWORD before first startup, or let the application create the initial administrator password file under DATA_DIR/secrets/initial-admin-password.

New installations and new accounts default to English. Imported accounts preserve their saved language preference. The UI also supports Polish, Spanish, and Norwegian.

Main configuration

Variable Default Purpose
DATA_DIR ./storage Database, backups, secrets, SSH known hosts
DATABASE_PATH <DATA_DIR>/routeros_backup_next.db SQLite database path
LISTEN_ADDR 127.0.0.1:5581 HTTP listener
API_PREFIX /api API base path
TIMEZONE Europe/Warsaw Scheduler timezone
SECRET_KEY generated/persisted when absent JWT signing secret
ALLOW_REGISTRATION false Allow account registration
DEFAULT_ADMIN_USERNAME admin Initial administrator name
DEFAULT_ADMIN_PASSWORD empty Initial administrator password
SSH_HOST_KEY_POLICY accept-new accept-new or strict
SSH_TIMEOUT_SECONDS 30 Device SSH timeout
MAX_PARALLEL_BACKUPS 4 Concurrent backup limit
CORS_ORIGINS [] Explicit allowed HTTP(S) origins

Run configuration validation without starting the server:

./target/release/mikrotik-backup check-config

Data layout

DATA_DIR/
├── routeros_backup_next.db
├── backups/
│   ├── exports/<device-id>/
│   └── binary/<device-id>/
├── secrets/
└── ssh/

RouterOS text exports are stored under backups/exports; binary RouterOS backups and SwOS .swb files are stored under backups/binary.

Legacy Python/TypeScript import

The importer is explicit and never runs during normal startup. Stop the old application first and import into a new Rust destination.

./import-python.sh /srv/mikrotik-backup-old

Docker Compose example:

docker compose run --rm \
  -v /srv/mikrotik-backup-old:/legacy:ro \
  app import-python --source /legacy

The importer copies accounts, devices, settings, logs, backup metadata, and every referenced backup file. It validates existing backup checksums when present and refuses to publish an incomplete destination. See docs/migration.md.

RouterOS and SwOS

RouterOS uses SSH-based export/backup operations. SwOS uses its web interface and downloads /backup.swb, with Digest authentication first and Basic as fallback. Connection testing distinguishes general web reachability from backup-endpoint availability.

SwOS details and troubleshooting are in docs/switchos.md.

Configuration comparison

.rsc files support:

  • side-by-side and unified diff;
  • changed-lines-only mode and search;
  • paged browser rendering to limit DOM size;
  • semantic RouterOS analysis that groups changes by configuration section and identifies added, removed, and modified commands/fields;
  • masking of sensitive values in semantic summaries.

Binary backups are not diffed.

Embedded web assets and caching

build.rs combines the UI sources listed in web/assets.list. Each compiled CSS/JavaScript bundle receives a content hash in its filename, for example:

/assets/app.<hash>.js
/assets/app.<hash>.css
/assets/docs.<hash>.js
/assets/docs.<hash>.css

The HTML shell is generated with those exact filenames. Hashed assets use:

Cache-Control: public, max-age=31536000, immutable

HTML and API responses remain non-cacheable by default. /favicon.ico intentionally returns 204 No Content.

API documentation

Interactive local documentation:

/docs

OpenAPI JSON:

/openapi.json

Swagger UI is embedded locally and does not require a CDN.

Validation

Frontend unit tests:

node web/tests/core.test.cjs

JavaScript syntax:

node --check web/js/i18n.js
node --check web/js/core.js
node --check web/js/views.js
node --check web/js/app.js

Rust checks when a toolchain is available:

cargo fmt --check
cargo test --locked
cargo clippy --locked --all-targets -- -D warnings

The browser smoke tests under web/tests/ require Playwright.

Security and dependencies

The service uses request size/time limits, JWT validation, password hashing, SSH host-key verification, secret redaction, restricted filesystem permissions, and a non-root container runtime. Dependency audit notes and the local libssh2-sys patch provenance are summarized in docs/security.md.

Third-party license notices are in docs/THIRD_PARTY_NOTICES.txt.