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 33.8%
  • CSS 3%
  • Dockerfile 1.2%
Find a file
JD 57712e552d
All checks were successful
release / release (push) Successful in 16s
release: v0.2.6 — coherent status color scheme
2026-06-21 03:20:43 +08:00
.forgejo/workflows security: pin the build supply chain (base image digests, Caddy+xcaddy modules, hashed pip deps, lazydocker checksum, CI) 2026-06-20 23:19:22 +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 feat(tls): import leaf+key+intermediate+root PEMs (ordered chain); doc/MANUAL-X509-CERT.md 2026-06-20 22:49:17 +08:00
doc docs: refresh status screenshot (coherent color scheme) 2026-06-21 03:17:53 +08:00
src release: v0.2.6 — coherent status color scheme 2026-06-21 03:20:43 +08:00
.gitignore feat(tls): manual certificate import provider (no ACME/token) + set-cert command; doc/MANAGE.md 2026-06-20 21:52:50 +08:00
CHANGELOG.md release: v0.2.6 — coherent status color scheme 2026-06-21 03:20:43 +08:00
DISCLAIMER.md docs: add Apache-2.0 LICENSE, NOTICE, and DISCLAIMER 2026-06-19 21:27:47 +08:00
docker-compose.yml feat(tls): manual certificate import provider (no ACME/token) + set-cert command; doc/MANAGE.md 2026-06-20 21:52:50 +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
README.md docs: refresh status screenshot (numbered style) + add deploy --all install screenshot 2026-06-21 02:53:17 +08:00
VERSION.md release: v0.2.6 — coherent status color scheme 2026-06-21 03:20:43 +08:00

packagemini — RouterOS firmware mirror

packagemini web UI

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.

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, WinBox, and an Atom feed (/feed.xml).
  • Atomic downloads — files are never half-served; in-progress versions stay hidden until complete.
  • 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.
  • Monitoring — JSON access logs, an optional GoAccess board at /goaccess, 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 git pull && sudo ./src/deploy.sh --all updates the mirror in place.

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
deploy.sh --all output

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 harden                 # enable/disable hardening measures (interactive)
sudo ./src/deploy.sh set-fqdn <fqdn>        # migrate to a new hostname (cert re-issues)
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 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, SYSTEM-HARDENING.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.