mirror.py rsync-server plugin
An event plugin for mirror.py that generates
rsyncd.conf and rsyncd.secrets from the live mirror package list plus a sidecar
rsync.json configuration file. The plugin regenerates both files automatically when the
mirror daemon starts (MASTER.INIT.POST) and whenever the package set changes via
mirror config reload (MASTER.CONFIG_RELOAD.POST).
How it works
The plugin listens on two mirror.py events:
MASTER.INIT.POST— fires once when the master daemon finishes starting up.MASTER.CONFIG_RELOAD.POST— fires aftermirror config reloadhas loaded a new configuration. Until the mirror.py core ships this event, regeneration happens at daemon start only; the listener signature accepts(*args, **kwargs)so it is forward-compatible.
File generation only. The plugin writes rsyncd.conf and rsyncd.secrets; it does
not start, stop, or signal the rsyncd process. This is intentional: rsyncd re-reads
rsyncd.conf and the secrets file on every incoming client connection, so adding or
removing mirror modules takes effect immediately with no reload signal needed.
Writes are atomic (via a temporary file in the same directory followed by os.replace).
If the generated content is identical to what is already on disk, the file is not
rewritten. Regeneration runs under a module-level lock so concurrent event firings do not
race.
Packages with settings.hidden: true are completely excluded — they appear in neither
the public nor the private module list.
Installation
Requires mirror.py >= 1.3.0 (uses the 1.3.0 plugin API: API version gate and the per-plugin config-file mechanism).
Install the plugin into the same Python environment as mirror.py:
pip install -e .
Enable the plugin in mirror.py's config.json. As of mirror.py 1.3.0 the plugins
block is enable-only:
"plugins": { "rsync-server": { "enabled": true } }
Any config sub-block here is ignored — the plugin's own settings live in rsync.json
(see the Configuration section below).
Scaffold rsync.json
The plugin supports mirror plugin config create:
mirror plugin config create rsync-server
This writes an example rsync.json (mode 0600, since it holds plaintext passwords).
Caveat: during mirror plugin config create the mirror config path is not loaded, so the
file is written to the fixed fallback location /etc/mirror/rsync.json regardless of
any --config flag. --config only points the command at a valid config.json so the
plugin registry can load; it does not change where the scaffold is written. If your
mirror config lives elsewhere, move the generated file next to your config.json, or copy
rsync.json.example manually. (At daemon runtime the config path
is known, so generation and reads use rsync.json next to your config.json.)
Configuration
rsync.json must live in the same directory as mirror.py's config.json (default path:
/etc/mirror/rsync.json).
See rsync.json.example for a complete, realistic example.
All keys are optional and fall back to the defaults listed below.
| Key | Type | Default | Description |
|---|---|---|---|
rsyncd_conf |
string (absolute path) | /etc/rsyncd.conf |
Destination path for the generated rsyncd.conf. |
secrets_file |
string (absolute path) | /etc/rsyncd.secrets |
Destination path for the generated secrets file. Also emitted as secrets file in the global section of rsyncd.conf. |
users |
object | {} |
Map of username to password. Written to the secrets file in insertion order. |
global |
object | {} |
Raw rsyncd parameter passthrough. Keys and values are emitted in insertion order as key = value lines in the global section. Values may be strings, integers, or booleans (booleans render as true/false). The key secrets file is managed by the plugin and is rejected if placed here. |
private_modules.enabled |
boolean | true |
When false, no [.Name] private module sections are generated. |
private_modules.auth_users |
string | * |
Value written to auth users in every private module section. |
private_modules.list |
boolean | false |
Value written to list in every private module section. |
private_modules.lock_file |
string (absolute path) | /var/run/rsyncd-private.lock |
Value written to lock file in every private module section. The private modules share this lock file, giving them a separate max-connections accounting pool independent of the public modules. |
Unknown top-level keys and unknown private_modules keys are rejected with a ValueError
at load time (typo protection). Any validation failure leaves previously generated files
untouched.
SECURITY NOTE: rsync.json contains plaintext passwords. Restrict its permissions:
chmod 600 /etc/mirror/rsync.json
The plugin logs a warning if rsync.json is group- or other-readable. The generated
rsyncd.secrets file is always written with mode 0600, as required by rsyncd's strict
modes.
Generated output
Given a package with id archlinux (display name ArchLinux), src = rsync://rsync.archlinux.org/ftp_tier1 and dst = /mirror/ftp/ArchLinux, and the global
settings from rsync.json.example, the plugin produces:
# WARNING: DO NOT EDIT!! Generated by the mirror.py rsync-server plugin.
uid = rsync
gid = nogroup
use chroot = no
max connections = 20
motd file = /mirror/etc/motd
log file = /var/log/geoul/rsyncd/all.log
transfer logging = yes
log format = %o %a - %u [%t] "%P/%f" - %l
pid file = /var/run/rsyncd.pid
exclude = .~tmp~
secrets file = /mirror/etc/rsyncd.secrets
[archlinux]
path = /mirror/ftp/ArchLinux
comment = from rsync://rsync.archlinux.org/ftp_tier1
[.archlinux]
path = /mirror/ftp/ArchLinux
comment = private module for ArchLinux without connection limits for authorized mirrors
auth users = *
list = false
lock file = /var/run/rsyncd-private.lock
Each package produces a public/private module pair:
[pkgid]is the public module. The module name is the package id, not the display name, so it is a valid rsyncd module token (no spaces or special characters). It is visible inrsync host::listings and carries acomment = from <upstream src>line. The comment line is omitted when a package has no upstreamsrc(for example, locally synced packages).[.pkgid]is the authenticated, unlisted private twin. The leading dot hides it fromrsync host::listings. Access is gated byauth usersand the secrets file. This module has its own lock file, so its connection accounting is independent of the public module'smax connectionslimit.
Modules are sorted by package id. The static header contains no timestamp so that identical content is detected and the file is not rewritten unnecessarily.
The secrets file (rsyncd.secrets) contains one username:password line per entry in
users, written in insertion order, with no header line.
Running rsyncd
Point rsyncd at the generated configuration file:
rsync --daemon --config=/etc/rsyncd.conf
Operational notes
Changing users or rsync settings. Edit rsync.json, then run:
mirror config reload
No daemon restart is needed for module or authentication changes. rsyncd picks up the
new rsyncd.conf and rsyncd.secrets on the next incoming client connection.
Parameters that require an rsyncd restart. Daemon-socket parameters such as port,
address, and pid file are read by rsyncd only at startup. Changing them in rsync.json
and running mirror config reload will update the generated file, but rsyncd itself must
be restarted for these to take effect. Module-level parameters (path, auth, comment, etc.)
apply per connection and do not require a restart.
Private module connection accounting. The private modules share the lock file
specified by private_modules.lock_file. This gives them a separate max-connections pool
rather than unlimited connections — the pool size is controlled by rsyncd's global
max connections parameter applied against that lock file independently from the public
modules' lock file.
Development
Set up a virtual environment and install the plugin alongside mirror.py:
uv venv
uv pip install -e /path/to/mirror.py -e . pytest
Run the test suite:
uv run pytest
License
Apache-2.0
Release files for mirror.py-rsync-server 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mirror_py_rsync_server-1.1.0.tar.gz | 22.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mirror_py_rsync_server-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.3 kB
Release files / mirror_py_rsync_server-1.1.0.tar.gz
| Download URL | mirror_py_rsync_server-1.1.0.tar.gz |
|---|---|
| Size | 22.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5168de43e5e34e9b2c7595eaa84ed3b56338de78fdaac4d909463cd813e4d6a8
|
|
BLAKE2b-256 checksum How to use checksums |
78e5cbff3eeb9a5ea0455415fb1a02ebbcc1f04811c48273fcd32fa52e5e0562
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency logRelease files / mirror_py_rsync_server-1.1.0-py3-none-any.whl
| Download URL | mirror_py_rsync_server-1.1.0-py3-none-any.whl |
|---|---|
| Size | 17.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0bf768d866d7bcbcd5fe015940e565821a8859246dbc64a614cc7df5febaf6da
|
|
BLAKE2b-256 checksum How to use checksums |
f97fab71d1c5fdcc698f17575b80703834b49e7f1a90f0803cda81dff996a944
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 6, 2026.
Transparency log