A self-hosted RouterOS firmware mirror that periodically fetches .npk packages and bundle zips from MikroTik's official release servers, serves them over HTTP/HTTPS via Caddy, and generates a themed static web UI plus an Atom feed.
  • Python 60.2%
  • Shell 36.4%
  • CSS 2.5%
  • Dockerfile 0.9%
Find a file
JD 598ecdecd4
All checks were successful
test / test (push) Successful in 23s
release / release (push) Successful in 22s
feat: machine-readable package index at /routeros/index.json (+ served schema)
New manifest.py builds an inventory (version -> arch -> packages+sizes, channel heads) from what's on disk; the sweep writes it atomically + human-readable on the final pass, alongside /routeros/index.schema.json (bundled copy of packagemini-schema.json, drift-tested). For automated consumers, starting with rosbackup-ng package discovery. Tests + doc/PACKAGE-INDEX.md.

Naming: data = index.json (per the schema's own title), contract = packagemini-schema.json / index.schema.json — not 'schema.json' (which would conflate data with contract).
2026-07-03 20:55:22 +08:00
.forgejo/workflows release: v0.11.0 — codebase audit sweep (5-agent): config/sweep/deploy bugs, docs, tests, CI 2026-07-03 17:35:29 +08:00
certs feat(tls): manual certificate import provider (no ACME/token) + set-cert command; doc/MANAGE.md 2026-06-20 21:52:50 +08:00
config release: v0.11.0 — codebase audit sweep (5-agent): config/sweep/deploy bugs, docs, tests, CI 2026-07-03 17:35:29 +08:00
doc feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00
src feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00
.gitignore release: v0.11.0 — codebase audit sweep (5-agent): config/sweep/deploy bugs, docs, tests, CI 2026-07-03 17:35:29 +08:00
CHANGELOG.md feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00
DISCLAIMER.md docs: add Apache-2.0 LICENSE, NOTICE, and DISCLAIMER 2026-06-19 21:27:47 +08:00
docker-compose.yml release: v0.12.0 — security hardening sweep (4-agent audit + docs cross-check) 2026-07-03 20:29:48 +08:00
LICENSE docs: add Apache-2.0 LICENSE, NOTICE, and DISCLAIMER 2026-06-19 21:27:47 +08:00
NOTICE docs: add Apache-2.0 LICENSE, NOTICE, and DISCLAIMER 2026-06-19 21:27:47 +08:00
packagemini-schema.json feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00
README.md feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00
VERSION.md feat: machine-readable package index at /routeros/index.json (+ served schema) 2026-07-03 20:55:22 +08:00

rosbackup-packagemini

rosbackup-packagemini

Self-hosted RouterOS firmware mirror

License: Apache-2.0 Rocky Linux 9 Mirrors RouterOS 7 Rootless containers

⚠️ Under active development — feature-incomplete until v1.0.
rosbackup-packagemini is usable today, but behavior and configuration may change between releases until v1.0 arrives.


A self-hosted RouterOS firmware mirror: it periodically fetches .npk packages and bundle zips from MikroTik's official servers, serves them over HTTP/HTTPS via Caddy, and generates a themed web UI plus an Atom feed. Routers fetch firmware from your mirror instead of download.mikrotik.com — just swap the hostname.

Standalone (no dependency on rosbackup-ng), rootless, and self-hardening, with a one-command Rocky Linux 9 installer that doubles as the manager.

packagemini web UI

The themed web UI — mirrored RouterOS versions grouped by channel (stable / long-term / testing / development), with release dates, per-file type chips, and a built-in legend. Point a router at it and just swap the hostname.

Features

  • Mirrors RouterOS firmware — choose channels (stable / long-term / testing / development), per-channel retention, architectures, and packages (main-only / named / all-extras).
  • Drop-in fetch paths/routeros/<version>/…, plus CHR images, netinstall (Windows + Linux), WinBox, and an Atom feed (/feed.xml).
  • Atomic downloads — files are never half-served; in-progress versions stay hidden until complete.
  • Scales to large fleets — parallel upstream fetching, plus host + Caddy tuning auto-scaled to the box (connection backlogs, conntrack, socket buffers, and file-descriptor limits sized to RAM / CPU cores) to serve firmware to hundredsthousands of routers concurrently.
  • Caddy front door — Let's Encrypt TLS via DNS-01 (deSEC, works on isolated hosts), rate limiting, IP allow/deny, optional Basic Auth, security headers.
  • In-place, version-safe updatesdeploy.sh update fetches all release tags and lets you pick a version (default: latest), rebuilds, and refuses downgrades; --force reinstalls the current version.
  • Monitoring — JSON access logs, an optional GoAccess board (on its own port), monthly reports.
  • Rootless & hardened — containers run as uid/gid 1000; the installer hardens the host (key-only SSH, fail2ban incl. a web-flood jail, automatic security updates, sysctl, firewall, tight file perms). See doc/SYSTEM-HARDENING.md.

Requirements

  • A Rocky Linux 9 host (also RHEL / AlmaLinux / CentOS Stream 9). The installer sets up Docker.
  • For HTTPS: a public FQDN and a deSEC API token (DNS-01).
  • Disk: ~3050 GB with the defaults — see disk sizing.

Install

Clone the repository onto the host and run src/deploy.sh as root. It installs Docker, hardens the host, opens the firewall, generates config, and brings up the stack. Idempotent.

# on the server (as root):
dnf install -y git
git clone https://git.jdneer.com/jd/rosbackup-packagemini.git /opt/packagemini
cd /opt/packagemini
sudo ./src/deploy.sh                 # first run creates packagemini.yml from the example and stops
$EDITOR packagemini.yml              # set public_base_url, channels, architectures, …
sudo ./src/deploy.sh                 # builds + starts the mirror

Cloning (rather than copying) also means ./src/deploy.sh status shows the exact commit, and sudo ./src/deploy.sh update upgrades the mirror in place (pick a version; latest by default). Install a specific version with --ref <tag|branch> (default: the latest release tag).

Everything in one go (mirror + GoAccess monitor + lazydocker TUI + sysadmin tools):

sudo ./src/deploy.sh --all
# à la carte: --with-monitor  --with-lazydocker  --with-tools  --docker-only  --no-harden  --no-tune  --ref <tag|branch>

--all (and a plain install) apply system hardening and performance tuning by default — opt out per stage with --no-harden / --no-tune. See doc/SYSTEM-HARDENING.md and doc/PERFORMANCE-TUNING.md.

The sync worker starts immediately and repeats on sync_interval (default 6 h); the web container serves files as soon as the first sweep writes them. The run is non-interactive once configured (preset any first-run answer via env for unattended installs — see ./src/deploy.sh --help).

Manage

src/deploy.sh is also the manager — run it from anywhere (it operates on the repo root):

sudo ./src/deploy.sh status                 # health, security, mirror config, content, sync
sudo ./src/deploy.sh start | stop | restart # compose lifecycle
sudo ./src/deploy.sh logs [service]         # follow logs
sudo ./src/deploy.sh refresh                # trigger a sync sweep now
sudo ./src/deploy.sh update [tag|branch]    # upgrade in place (pick a version; default: latest)
sudo ./src/deploy.sh --force                # rebuild/reinstall the current version
sudo ./src/deploy.sh harden                 # toggle security hardening measures (interactive)
sudo ./src/deploy.sh tune [--remove]        # apply / remove the host performance profile (scaled to RAM/cores)
sudo ./src/deploy.sh set-fqdn <fqdn>        # migrate to a new hostname (cert re-issues)
sudo ./src/deploy.sh set-timezone <zone>    # host + release-date timezone
sudo ./src/deploy.sh set-cert               # import/replace a manual TLS cert (renewals)
sudo ./src/deploy.sh set-mirror-auth on|off # HTTP basic auth on the mirror
sudo ./src/deploy.sh set-operator           # footer + User-Agent operator name/email (public)
sudo ./src/deploy.sh open-goaccess          # print the GoAccess board URL
sudo ./src/deploy.sh clear-config [data]    # uninstall / reconfigure

Full workflow + every command: doc/MANAGE.md (or ./src/deploy.sh --help).

Point a router at the mirror

/tool fetch url="https://<mirror>/routeros/<ver>/routeros-<ver>.npk" mode=https

The listing at https://<mirror>/routeros/<ver>/ shows every file for that version.

Configure

Settings live in packagemini.yml (copy config/packagemini.example.yml); secrets go in .env (config/env.example), managed by deploy.sh. Every option is annotated in the example file. Backup/restore is just copying the host-bound data/, logs/, reports/ directories.

Documentation

Repository layout

docker-compose.yml          # the stack (build contexts point at src/*)
src/  deploy.sh, sync/, web/, monitor/      # installer/manager + container build contexts
config/  packagemini.example.yml, env.example
doc/  DESIGN.md, MANAGE.md, SYSTEM-HARDENING.md, PERFORMANCE-TUNING.md, UPDATES.md, MANUAL-X509-CERT.md, TODO.md

Runtime files (packagemini.yml, .env, data/, logs/, reports/) live at the repo root and are gitignored; the live .env stays there so docker-compose auto-loads it.

License

Apache-2.0 — see LICENSE, NOTICE, and DISCLAIMER.md.