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 62%
  • Shell 34.8%
  • CSS 2.4%
  • Dockerfile 0.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
JD 0c97a17eb3
All checks were successful
test / test (push) Successful in 5m12s
release / release (push) Successful in 25s
docs: refresh web UI + status screenshots; show status in the README
2026-09-18 21:34:21 +08:00
.forgejo/workflows chore: default branch is now master (CI trigger, deploy.sh fallback, docs) 2026-09-18 16:58:55 +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 docs: refresh web UI + status screenshots; show status in the README 2026-09-18 21:34:21 +08:00
src fix: refresh no longer leaves sync down after a reboot 2026-09-18 21:28:09 +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 release: v1.0.0 — fold the refresh fix and master rename into the release notes 2026-09-18 21:31:23 +08:00
DISCLAIMER.md docs: add Apache-2.0 LICENSE, NOTICE, and DISCLAIMER 2026-06-19 21:27:47 +08:00
docker-compose.yml docs: say "non-root containers", not "rootless" 2026-09-18 16:55:25 +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 docs: refresh web UI + status screenshots; show status in the README 2026-09-18 21:34:21 +08:00
VERSION.md chore: default branch is now master (CI trigger, deploy.sh fallback, docs) 2026-09-18 16:58:55 +08:00

rosbackup-packagemini

rosbackup-packagemini

Self-hosted RouterOS firmware mirror

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


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), non-root containers, and self-hardening, with a one-command Rocky Linux 9 installer that doubles as the manager.

packagemini web UI

The themed web UI — every mirrored RouterOS version with its size, release date and channel (stable / long-term / testing / development), filterable per channel, plus WinBox and an RSS feed. 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, verified downloads — every file is checked against upstream's size and resumed when the connection drops, so files are never half-served; in-progress versions stay hidden until complete, and deploy.sh verify re-checks the whole mirror on demand.
  • 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 hundreds–thousands 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 updates — deploy.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.
  • Non-root containers & hardened host — 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: ~30–50 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 verify                 # check mirrored files against upstream, re-fetch bad ones
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).

deploy.sh status output

deploy.sh status — one screen for the whole deployment: version, security posture, performance profile, container health, mirror/TLS state, effective config, content on disk, and sync activity.

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.