PM Software Stack
PM Software Stack (PMSS) is an open-source server management stack that turns a Debian server into a multi-tenant seedbox. It handles user provisioning, application installation, traffic accounting, resource isolation, and ongoing maintenance through automated cron jobs. PMSS is licensed under the GNU General Public License v3 and hosted on GitHub at https://github.com/MagnaCapax/PMSS under the MagnaCapax organization.
PMSS is free to use for anyone. There are no commercial restrictions. Third parties may use it to provide seedbox services, and minimalistic whitelabeling features are included. The project is primarily written in PHP (49.5%), JavaScript (37.5%), CSS (7.6%), and Shell (4.9%).
Overview
PMSS manages the full lifecycle of a seedbox server. It installs and configures torrent clients, web servers, VPN endpoints, media applications, and supporting tools. It provisions isolated user accounts with disk quotas, traffic limits, and cgroup-based resource controls. Cron-driven watchdogs keep services running. A two-phase update system allows the fleet to be upgraded without breaking existing users.
The stack runs on Debian 10, 11, and 12. Debian 10 is considered stable, Debian 12 is in production, and Debian 13 (trixie) is experimental. Each user gets a dedicated lighttpd process, a dedicated rTorrent (or Deluge/qBittorrent) instance, per-user cgroup resource limits, and rootless Docker.
PMSS supports the following primary torrent clients:
- rTorrent with ruTorrent web interface (default)
- Deluge with web UI
- qBittorrent (qbittorrent-nox) with web UI
Media applications, Usenet tools, cloud sync, VPN, and other services are also supported (see Supported applications).
Production use
Pulsed Media uses PMSS across its entire seedbox fleet. As of early 2026, PMSS manages over 80 production servers across two datacenters in Finland (Helsinki and Kerava), handling hardware ranging from 1 Gbps shared plans on RAID-5 arrays to 10 Gbps dedicated servers with NVMe caching. The same codebase runs on both bare-metal hosts and Proxmox virtual machines.
The fleet spans multiple Debian releases simultaneously (10, 11, and 12 in production), which is why PMSS maintains per-release dpkg baselines and template sets. Third parties also use PMSS to run their own seedbox hosting services under the GPL-3.0 license.
History
PMSS started as an internal tool at Pulsed Media for managing seedbox servers. The install.sh copyright header dates to 2010. Before PMSS, seedbox provisioning was done by hand — each user account set up manually, each torrent client configured individually. PMSS automated this into a repeatable process. The project was later open-sourced under GPL-3.0 through the MagnaCapax GitHub organization, making it available to anyone who wants to run a multi-tenant seedbox server.
Milestones based on the changelog and repository history:
| Year | Event |
|---|---|
| 2010 | Initial development of install.sh bootstrap |
| 2011 | Earliest public changelog entries on the wiki |
| 2015 | Conversion to per-user lighttpd processes (from a single shared httpd). Public web hosting directories added. ruTorrent updated to 3.7. rTorrent updated to 0.9.6/0.13.6. OpenVPN support added. Sonarr/SABnzbd integration. Cgroup resource control introduced. |
| 2016 | Debian 8 (jessie) support. Rclone, Syncthing, FileBot added. FireHOL firewall suite compiled from source. IOstat and SMART monitoring cron jobs added. |
| 2019 | rTorrent bumped to 0.9.8/0.13.8. Let's Encrypt automation. WireGuard configuration (later releases). Bonding interface support. ruTorrent updated to 3.9. |
| 2020 | Debian 10 production support. Deluge installation and configuration. qBittorrent support. Tracker cleaner for problematic public trackers. FireQOS traffic shaping. ruTorrent updated to 3.10. New file manager. |
| 2021 | qBittorrent cron watchdog. Rclone GUI integration. Lighttpd streaming config. Disk I/O and traffic monitoring improvements. |
| 2022 | Systemd user slice cgroup limits (CPU weight, IO weight, memory soft/hard limits). Rootless Docker per user. Custom lighttpd config directory (~/.lighttpd/custom.d/). Custom GUI frames. ruTorrent themes from community contributions. MediaInfo repo integration.
|
| 2023 | Development moved to GitHub. Docker rootless + docker-compose per user. ~/bin added to PATH. Changelog continues on GitHub.
|
| 2024-2025 | Major refactoring: two-phase updater architecture, dpkg baseline snapshots per Debian release, modular app installers under scripts/lib/update/apps/, cgroup v2 support with dual-path detection, WireGuard installer with hermetic tests, install-media-stack.sh for Servarr/Jellyfin/SABnzbd. 143 development tests. CI via GitHub Actions. Architecture Decision Records (ADRs). Dist-upgrade automation (Debian 10 to 11 to 12).
|
| 2026 | Continued active development. Over 1100 commits, 9 contributors. Agentic CI pipeline (GitHub Actions tests on every push). 82-server fleet upgraded to Debian 12 via automated dist-upgrade. |
Installation
PMSS requires a minimal Debian installation (10, 11, or 12). The installer is run as root.
Quick install (latest release)
Download and run the installer:
wget -qO /tmp/pmss-install.sh https://github.com/MagnaCapax/PMSS/raw/main/install.sh bash /tmp/pmss-install.sh
The project README also documents a one-liner that pipes wget output directly to bash. See the README for that variant.
Install from git main branch
wget -qO /tmp/pmss-install.sh https://github.com/MagnaCapax/PMSS/raw/main/install.sh bash /tmp/pmss-install.sh git/main
Pin a specific release
wget -qO /tmp/pmss-install.sh https://github.com/MagnaCapax/PMSS/raw/main/install.sh bash /tmp/pmss-install.sh release:2023-07-22
What the installer does
install.sh is a thin bootstrapper. Its responsibilities are limited to:
- Ensure core tooling is present: PHP CLI, git, curl, wget, ca-certificates, rsync, unzip, vim, build-essential, autoconf, automake, pkg-config, libtool, subversion.
- Prompt for hostname configuration (
/etc/hostname). The hostname must be a fully-qualified domain name (FQDN) because PMSS reads it for certbot, proftpd, and nginx SSL configuration. - Configure
/etc/fstabfor disk quotas on/home. PMSS expects/hometo be a dedicated filesystem with quota options:usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv1. Performance mount options (nofail,noatime,lazytime,commit=30) are also applied for ext4. - Apply multi-tenant hardening:
/procmounted withhidepid=2(so users cannot see each other's processes), andsystemd.unified_cgroup_hierarchy=0added to GRUB for rootless Docker compatibility. - Download the PMSS snapshot (git clone or release tarball) and deploy it to
/scripts,/etc, and/var. - Hand off to
/scripts/update.phpfor the main configuration work. - Run post-install setup:
setupRootCron.php,setupSkelPermissions.php,quotaFix.php,ftpConfig.php.
Installer flags
| Flag | Description |
|---|---|
--hostname=<name> |
Set hostname without interactive prompt |
--skip-hostname |
Keep the current hostname |
--quota-mount=<path> |
Inject quota options for the specified fstab mount |
--skip-quota |
Skip quota configuration |
--non-interactive |
Skip all interactive prompts (hostname and quota) |
--skip-upgrade |
Skip the initial apt full-upgrade
|
--dry-run |
Print the plan without changing the system |
--skip-update |
Stop after staging files; do not run update.php
|
--scripts-only |
Pass through to update.php --scripts-only
|
The installer performs a preflight check requiring at least 2 GiB free disk space on /. If PMSS is already installed, the installer detects existing markers and hands off to update.php directly.
Logs are written to /var/log/pmss-install.log. The update phase logs to /var/log/pmss/update.log and /var/log/pmss-update.jsonl.
Disk space requirements
The installer needs at least 2 GiB free on the root partition. The /home partition should be sized for user data storage. PMSS itself uses approximately 500 MB for scripts, applications, and compiled binaries.
Architecture
Directory layout
PMSS organizes files across several standard Linux locations:
| Path | Purpose |
|---|---|
/scripts/ |
Main PMSS scripts: user management, update system, utilities |
/scripts/cron/ |
Cron job scripts (watchdogs, monitoring, traffic accounting) |
/scripts/util/ |
Utility scripts (network setup, lighttpd config, Let's Encrypt, user transfer) |
/scripts/lib/ |
Shared PHP libraries (traffic, resources, cgroup, rtorrent config, update modules) |
/scripts/lib/update/ |
Phase 2 update modules (distro detection, package management, app installers, web stack) |
/scripts/lib/update/apps/ |
Application-specific installers (rtorrent, deluge, docker, sonarr, radarr, etc.) |
/scripts/lib/update/dpkg/ |
Per-release dpkg baseline snapshots (selections-debian10.txt, selections-debian11.txt, selections-debian12.txt)
|
/scripts/lib/tests/ |
Test suites (development and production tiers) |
/scripts/testing/ |
Test runner scripts (test-php.sh, test-bash.sh, test-all.sh, lint tools)
|
/etc/seedbox/config/ |
Configuration templates and runtime config (see Configuration) |
/etc/seedbox/runtime/ |
Runtime state (port assignments, etc.) |
/etc/seedbox/skel/ |
Skeleton files for new user accounts |
/etc/skel/ |
User skeleton: default .bashrc, web GUI files, media stack installer, bin scripts
|
/var/log/pmss/ |
Log directory for all PMSS cron jobs and operations |
/var/www/ |
Default web server error pages and status endpoint |
/home/<user>/ |
Per-user home directories containing data, sessions, configs, and web content |
Two-phase update system
PMSS updates run in two phases:
Phase 1 (scripts/update.php):
- Parses the requested source spec (
git/<branch>,release[:tag], or a custom repository) - Fetches the snapshot (shallow git clone or release tarball)
- Stages the snapshot by replacing
/scriptsand refreshing/etcand/var - Records the version under
/etc/seedbox/config/versionandversion.meta - Re-runs itself if the fetched snapshot updated
update.php - Invokes phase 2
Phase 2 (scripts/util/update-step2.php):
- Detects distro name, version, and codename
- Enforces non-interactive apt settings and finishes pending dpkg configurations
- Refreshes APT repositories and installs all queued packages (this ordering is mandatory)
- Prepares the host: cgroups, systemd slices, base permissions, MOTD, locales
- Runs application installers under
scripts/lib/update/apps/*.php - Configures the web stack (lighttpd/nginx)
- Updates every user environment and rescans skeletons
- Reapplies network templates and security hardening
- Logs completion markers
Every step flows through a shared runStep() helper that logs to /var/log/pmss-update.log, records JSON events to /var/log/pmss-update.jsonl, and collects profiling metadata.
Update commands
# Update to latest release /scripts/update.php release # Update to git main branch /scripts/update.php git/main # Deploy scripts only (skip phase 2, useful for hotfixes) /scripts/update.php git/main --scripts-only # Dry-run (log without changes) /scripts/update.php release --dry-run # Dist-upgrade to Debian 12 /scripts/update.php --dist-upgrade=12
The updater self-refreshes from GitHub at the start of every run. Version specs normalize user input so main, git main, and git/main produce identical results.
Dist-upgrade automation
PMSS can automate Debian major version upgrades (e.g., Debian 10 to 11, or 11 to 12) through the --dist-upgrade flag:
# Upgrade to Debian 12 (from whatever version is currently running) /scripts/update.php git/main --dist-upgrade=12 # Upgrade one step (e.g., 10 to 11) /scripts/update.php git/main --dist-upgrade=11
The --dist-upgrade=N parameter sets the maximum target Debian version. PMSS handles source list updates, non-interactive apt configuration, pending dpkg resolution, and the full-upgrade itself. After the OS upgrade completes, PMSS continues into its normal phase 2, which reinstalls and reconfigures all applications for the new Debian release.
The dist-upgrade always runs through update.php as the entry point. This is mandatory because update.php handles script self-update, cron backup, event logging, and phase 2 continuation that a standalone upgrade script would skip. JSON log events (dist_upgrade_start, dist_upgrade_end) are recorded for audit.
Each Debian release has a corresponding APT sources template (template.sources.buster, template.sources.bullseye, template.sources.bookworm, template.sources.trixie) and a dpkg baseline snapshot (selections-debian10.txt through selections-debian12.txt) that define the expected package set. After upgrade, the baseline snapshot for the new release is applied to pull in any packages that differ between versions.
In February 2026, this mechanism was used to upgrade 82 Pulsed Media production servers from Debian 10 to Debian 12 with zero data loss.
Recovery from failed upgrades
If a dist-upgrade fails mid-way (SSH dropped, dpkg hung on config prompts, PHP missing), recovery follows these steps:
- Gain shell access via KVM, IPMI, or VNC console
- Kill suspended apt/dpkg processes:
killall apt apt-get dpkg - Resume dpkg configuration:
DEBIAN_FRONTEND=noninteractive dpkg --configure -a - Restore network if interface names changed (check
ls /sys/class/net, update/etc/network/interfaces) - Reinstall PHP if missing:
apt-get install -y php-cli php-curl php-json php-xml php-mbstring php-zip - Finish the upgrade:
apt-get -f install && apt-get full-upgrade -y - Verify services:
systemctl status sshd proftpd nginx
ProFTPD sometimes hangs during dist-upgrade on config file prompts. The fix is to force the old config: apt-get install -o Dpkg::Options::="--force-confold" proftpd-basic.
Full recovery procedures are documented in docs/recovery.md.
Config template system
PMSS uses template files stored in /etc/seedbox/config/ to generate service configurations. Templates use placeholder tokens (e.g., ##memoryMax, ##username) that are substituted at provisioning or update time. Generated configs are written to system locations (/etc/nginx/, /etc/proftpd/, user home directories). The template system is idempotent: running the configuration scripts multiple times converges on the same state.
Supported applications
PMSS installs and manages the following applications. Each has a dedicated installer module under scripts/lib/update/apps/ and, where applicable, cron watchdogs and user-facing GUI integration.
Torrent clients
| Application | Description | Installer module | Cron watchdog |
|---|---|---|---|
| rTorrent + libtorrent | The default torrent client. Compiled from source along with xmlrpc-c. Per-user instances managed via .rtorrent.rc config and SCGI socket. |
rtorrent.php |
checkRtorrent.php (every 2 minutes)
|
| ruTorrent | Web-based frontend for rTorrent. Served through per-user lighttpd. Includes themes (MaterialDesign default), plugins (RSS, autodl, hddquota, etc.), and a file manager. | Deployed via /etc/skel/www/rutorrent/ |
checkGui.php (every 10 minutes)
|
| Deluge | Alternative torrent client with web UI. On Debian 10, built from source with pip; on newer releases, installed from apt packages. Accessed via nginx reverse proxy at /deluge-USERNAME/. |
deluge.php |
checkDelugeInstances.php (every minute)
|
| qBittorrent | qbittorrent-nox with WebUI. Configured with randomized external port. Reverse-proxied through lighttpd. | Installed via packages.php, configured in userConfig.php |
checkQbittorrentInstances.php (every 2 minutes)
|
Media applications
PMSS provides two paths for media application installation: a native per-user media stack installer, and rootless Docker containers.
Native media stack (install-media-stack.sh)
The install-media-stack.sh script in /etc/skel/ installs a self-contained media stack in each user's home directory under ~/.bin with configs under ~/.config. It installs:
| Application | Type | Runtime |
|---|---|---|
| Radarr | Movie manager (Servarr) | .NET 8 |
| Sonarr v4 | TV show manager (Servarr) | .NET 8 |
| Prowlarr | Indexer manager (Servarr) | .NET 8 |
| Jellyfin | Media server | .NET 8 |
| SABnzbd | Usenet downloader | Python (venv) |
| Cloudplow | Cloud upload automation | Python (venv) |
All apps bind to 127.0.0.1 and are reverse-proxied by per-user lighttpd to https://<host>/public-<user>/<app>/. The installer is idempotent, supports --dry-run, and handles GLIBC compatibility on older Debian releases by pinning Radarr versions.
Usage:
# Install (from local copy) bash install-media-stack.sh # Dry-run (verify URLs only) bash install-media-stack.sh --dry-run # Pin Radarr version on Debian 11 bash install-media-stack.sh --radarr-pin=v5.10.4.9218
The installer can also be fetched directly from GitHub. See the install-media-stack documentation for all options.
System-level application installers
Phase 2 of the update also installs these applications system-wide:
| Application | Installer module | Description |
|---|---|---|
| Sonarr | sonarr.php |
Installed under /opt/Sonarr via GitHub Releases API
|
| Radarr | radarr.php |
Installed under /opt/Radarr via GitHub Releases API
|
| Mono | mono.php |
.NET runtime for legacy Sonarr, installed from Debian packages |
| FileBot | filebot.php |
Media renaming tool, installed from deb package (v4.9.4) |
| pyLoad | pyload.php |
Download manager, installed in /opt/pyload virtualenv
|
Cloud and sync
| Application | Installer module | Description |
|---|---|---|
| rclone | rclone.php |
Cloud storage sync. Binary installed from rclone.org. GUI integration with user-controllable start/stop. Cron watchdog: checkRcloneInstances.php (every 2 minutes).
|
| Syncthing | syncthing.php |
Encrypted file sync between devices. Binary installed from PMSS remote package host. |
| BTSync / Resilio | btsync.php |
BTSync 1.4 and Resilio 2.2 binaries maintained under /usr/bin.
|
| acd_cli | acdcli.php |
Amazon Cloud Drive CLI, installed in /opt/acd_cli virtualenv.
|
| FlexGet | python.php |
RSS/automation tool, installed via pip in a virtualenv. |
VPN
| Application | Description |
|---|---|
| WireGuard | Host-level VPN endpoint at /etc/wireguard/wg0.conf. Keys auto-generated. Per-user peer config via ~/.wireguard-public-key. Cron watchdog: checkWireguard.php (every 5 minutes). Peer refresh: wireguardPeersRefresh.php (every 6 minutes). Each user gets a unique /32 address inside 10.90.90.0/24. Documented in docs/wireguard.md.
|
| OpenVPN | EasyRSA-based server/client configs. Client bundles written to /etc/skel/www. Currently disabled on newer releases pending support updates.
|
Docker
PMSS provisions rootless Docker for each user account. The Docker daemon runs without root privileges under each user's namespace. Details:
- Docker CE with buildx and compose plugins, installed from Docker's official APT repository
- User namespaces (
subuid/subgid) configured per user DOCKER_HOSTenvironment variable set in~/.bashrc- Storage driver:
fuse-overlayfspreferred (written to~/.config/docker/daemon.json) - Watchdog:
checkRootlessDocker.php(every 5 minutes) - Helper:
~/bin/docker-install-wireguard.shfor linuxserver.io WireGuard container - Operator control:
/scripts/userDocker.php USER {start|stop|restart|status}
Documentation for running LinuxServer.io containers is included in docs/linuxserver.io.md.
Other tools
| Application | Description |
|---|---|
| vnStat | Network traffic monitoring, configured for the detected uplink interface |
| FireHOL | Firewall suite, compiled from source (v3.1.6) |
| iprange | IP range tool, compiled from source (v1.0.4) |
| mediainfo | Media file metadata tool, installed from MediaArea repository |
| certbot | Let's Encrypt certificate automation |
User management
Adding a user
/scripts/addUser.php USERNAME PASSWORD MAX_RAM_MB DISK_QUOTA_IN_GB [trafficLimitGB]
Parameters:
USERNAME-- login name. Normalized to lowercase. Must match[a-z][a-z0-9]{2,7}(3-8 characters, starting with a letter).PASSWORD-- initial password. Userandfor a random password.MAX_RAM_MB-- account memory limit, used for cgroup limits and rTorrent tuning.DISK_QUOTA_IN_GB-- storage quota.trafficLimitGB(optional) -- monthly traffic cap.
On success, addUser.php:
- Creates the Unix user and home directory
- Assigns an HTTP service port via
portManager.php - Writes rTorrent and ruTorrent configuration
- Enables quotas and traffic limits
- Applies cgroup resource defaults
- Starts rTorrent and lighttpd
- Emits a summary marker to stdout and
/var/log/pmss/addUser.log
A per-user lock file prevents concurrent addUser runs for the same username. Provisioning fails fast if the user already exists or critical steps fail.
Example:
/scripts/addUser.php alice rand 512 100 500
This adds user alice with a random password, 512 MB memory limit, 100 GB disk quota, and 500 GB monthly traffic limit.
Changing user settings
/scripts/util/userConfig.php USERNAME MAX_RAM_MB DISK_QUOTA_IN_GB [TRAFFIC_LIMIT_GB] [CPUWEIGHT] [IOWEIGHT] [CPUQUOTAPCT]
This rewrites rTorrent and ruTorrent configs, applies disk quota changes, updates cgroup limits, and restarts the user's rTorrent process. Optional parameters allow setting CPU weight, IO weight, and CPU quota percentage for the user's systemd slice.
Suspending and unsuspending
/scripts/suspend.php USERNAME /scripts/unsuspend.php USERNAME
Suspension stops all user processes, disables login, and serves a suspension notice page. PMSS uses an epoch (1970-01-01) account expiry date as the suspension marker. Cron watchdogs skip suspended users.
Terminating a user
/scripts/terminateUser.php USERNAME [--confirm]
Terminates the user account. Kills all user processes, removes lighttpd and nginx configs, releases the assigned port, reverts the cgroup slice, and deletes the home directory. The --confirm flag is required for non-interactive runs.
Recreating a user
/scripts/recreateUser.php USERNAME
Rebuilds a user's environment without losing data. Copies the htpasswd file from the old configuration.
Transferring a user between servers
userTransfer.php migrates a user account from one PMSS server to another over SSH. The script always runs on the destination server and pulls data from the source. It never pushes.
# Basic transfer (same username on both servers) /scripts/util/userTransfer.php alice source-server.pulsedmedia.com # Transfer with username change (local alice, remote bob) /scripts/util/userTransfer.php alice bob source-server.pulsedmedia.com
The password for the remote server can be passed via the PMSS_USER_TRANSFER_PASSWORD environment variable. If not set, the script prompts interactively with echo disabled and requires double-entry confirmation.
How the transfer works
The transfer runs in two phases with multiple passes each:
Main passes (default 31): rsync pulls the user's home directory from the source, excluding volatile files (session data, GUI state, lighttpd configs, traffic counters, cache). Each pass picks up files that changed since the last pass. Random sleep intervals (60-360 seconds by default) space out the passes to avoid saturating the network.
Final passes (default 3): rsync pulls the volatile paths that were excluded during main passes: session/, www/rutorrent/share/, ~/.lighttpd/custom.d/, ~/.local/, and www/public/.
After both phases complete, the script normalizes file ownership via userPermissions.php and creates an rTorrent restart marker so the torrent client picks up the migrated session data.
Transfer options
| Option | Default | Description |
|---|---|---|
--main-passes N |
31 | Number of bulk rsync passes |
--final-passes N |
3 | Number of volatile-path rsync passes |
--sleep-min N |
60 | Minimum seconds between passes |
--sleep-max N |
360 | Maximum seconds between passes |
--no-sleep |
-- | Disable sleep between passes |
--dry-run |
-- | Log planned steps without executing |
--print-password |
-- | Print password at end (not recommended) |
Safety measures
The transfer script has multiple safety checks:
- Must run as root
- Refuses to transfer system users (UID < 1000)
- Refuses if the local home directory is a symlink
- Validates the home path against
/etc/passwd - Uses
rsync -avwithout--delete(never removes files on the destination) - Temporary Expect scripts are stored in a root-only (mode 0700) scratch directory and cleaned up on exit
- Passwords are cleared from environment after use
- Path-escape check before renaming ruTorrent user directories
If the remote and local usernames differ, the script renames the ruTorrent share directory to match the local username after transfer.
All steps are logged to /var/log/pmss/userTransfer.log with timestamped prefixes ([START], [INFO], [WARN], [OK], [SKIP]).
For large accounts (multi-terabyte), transfers can run for days. The PMSS_COMMAND_TIMEOUT environment variable controls the maximum runtime. Set it high (e.g., 172800 for 48 hours) or the script disables it internally for the duration of the transfer.
Other user management tools
| Script | Description |
|---|---|
listUsers.php |
List managed tenant usernames, one per line |
changePw.php |
Change a user's password (updates per-user htpasswd file) |
showTraffic.php |
Show per-user traffic summary (--json available)
|
showResources.php |
Show per-user resource usage |
userTorrents.php |
Count torrents per user (--by-client for breakdown)
|
userSetting.php |
View or modify per-user settings |
userSlice.php |
View user systemd slice status |
wireguardPeersStatus.php |
Show WireGuard peer connection status |
Web interface
Each PMSS user has a web-accessible interface served by their dedicated lighttpd process, reverse-proxied through nginx. The interface is available at https://SERVER/user-USERNAME/ and protected by HTTP Basic authentication.
GUI tabs and features
The default GUI (/etc/skel/www/) provides:
- ruTorrent -- Full ruTorrent web interface for rTorrent, with themes (MaterialDesign default) and plugins including RSS, autodl-irssi, hddquota, autotools, unpack, and a retracker plugin.
- File Manager -- Web-based file browser for downloading files from the seedbox. Enforces HTTPS.
- Info/Stats -- Server statistics page showing RAM usage, disk quota, traffic consumption with daily charts (Chart.js), and IP information.
- Welcome -- Overview page with quota warnings, traffic limit meter, announcements RSS feed, download links for OpenVPN/WireGuard configs, and application control buttons (rTorrent restart, lighttpd restart).
- Deluge -- Deluge web UI tab (shown when enabled via
~/.delugeEnable) - qBittorrent -- qBittorrent web UI tab
- rclone -- rclone web GUI tab with start/stop controls
- Custom frames -- User-definable tabs via
~/.customFramesfile (format:appname|tooltip|label|url, one per line)
Public web hosting
Each user has a public web hosting directory at ~/www/public/, accessible without authentication at https://SERVER/public-USERNAME/. Users can host static files or PHP applications in this directory.
Whitelabeling
PMSS includes basic whitelabeling support for third-party operators who want to run their own seedbox service. The GUI header, footer, welcome text, and announcements RSS feed URL can be customized through configuration files. Custom CSS and JavaScript can be added to the GUI skeleton in /etc/skel/www/. The MOTD template at /etc/seedbox/config/template.motd can be rewritten with the operator's own branding.
Since PMSS is GPL-3.0, the only licensing requirement is that modified versions remain open-source if distributed. Operators who run PMSS on their own servers without distributing the code have no obligation to publish changes.
Custom lighttpd configuration
Users can add custom lighttpd directives in ~/.lighttpd/custom.d/*.conf. These files are included by the main per-user lighttpd config. A lighttpd restart (via the GUI button or SIGUSR1) is required to load changes.
Per-user PHP
Each user gets a per-user php.ini at ~/.lighttpd/php.ini with settings optimized for seedbox use: 512 MiB memory limit, 16 GiB max upload file size, 50 max simultaneous uploads, 90-second max execution time.
Network and traffic management
Traffic accounting
PMSS tracks per-user traffic using iptables owner-match rules. The setupNetwork.php script installs monitoring rules that tag packets by user. Traffic data is collected and aggregated by cron jobs:
trafficLog.php-- Captures traffic counters every 5 minutestrafficIngressLog.php-- Captures ingress traffic counters every 5 minutestrafficStats.php-- Aggregates raw logs into monthly statistics (twice per hour)trafficIngressStats.php-- Aggregates ingress logs (twice per hour)trafficLimits.php-- Enforces per-user traffic limits (hourly)
Daily traffic consumption is saved to each user's ~/.trafficData file for display in the GUI stats page. Local network traffic (defined in /etc/seedbox/config/localnet) is tracked separately.
Bandwidth shaping
PMSS uses FireQOS for per-user bandwidth shaping. The setupNetwork.php script generates a FireQOS configuration from the template at /etc/seedbox/config/template.fireqos. Users are assigned balanced priority by default.
Traffic limits can be set per user:
/scripts/util/userTrafficLimit.php USERNAME LIMIT_GB
When a user exceeds their traffic limit, progressive throttling can be applied. This is controlled by the progressiveThrottleEnabled, progressiveThrottleFloorPercent, and progressiveThrottleGracePercent settings in /etc/seedbox/config/network.
Firewall
setupNetwork.php configures iptables rules including:
- Bogon/martian network drop filters
- NAT and forwarding for VPN tunnels (WireGuard and OpenVPN)
- Per-user traffic monitoring rules
- Connmark-based packet tagging
- TCP SACK mitigation rules
The network configuration file at /etc/seedbox/config/network defines the interface name and link speeds. The local network file at /etc/seedbox/config/localnet defines internal network ranges (defaults to 185.148.0.0/22).
Security
User isolation
PMSS isolates users at multiple levels:
- Process visibility:
/procis mounted withhidepid=2so users cannot see each other's processes. - File permissions:
/homehaso-rwpermissions. Each user's home directory is owned by that user with restricted group and other permissions (750). User permissions are managed byuserPermissions.php. - Per-user lighttpd: Each user has their own lighttpd + php-cgi process pair, running as that user. No shared web server process.
- Per-user rTorrent/Deluge/qBittorrent: Torrent client instances run under the user's own UID.
- Rootless Docker: Docker daemons run without root privileges, using user namespace mapping.
- SCGI socket: rTorrent communicates via a per-user Unix socket (
~/.rtorrent.socket) with 770 permissions.
Cgroup resource control
PMSS uses Linux cgroups (via systemd user slices) for per-user resource limits. On Debian 11/12, cgroup v2 (unified) is used. On Debian 10, cgroup v1.
Default limits:
- MemoryHigh: >= 250 MiB floor, ~10% of total RAM
- MemoryMax: min(1.5x MemoryHigh, 95% of total RAM)
- CPUWeight: 200
- IOWeight: 200
- TasksMax: 4096
- CPUQuota: 85% of total logical cores
Root (user-0.slice) is always unlimited. A boot-time check and periodic cron job (cgroupRootCheck.php) enforce this.
Per-host overrides can be set in /etc/seedbox/config/cgroup.policy.php:
<?php return [ 'cpuWeight' => 100, 'ioWeight' => 100, 'tasksMax' => 2048, 'memoryHighMiB' => 500, 'memoryMaxMiB' => 750, ];
Per-user adjustments:
# View current limits /scripts/util/userConfigCgroup.php alice --status # Apply custom limits /scripts/util/userConfigCgroup.php alice --apply --cpu-weight=300 --memory-high=600 # Apply IO throttles for HDD /scripts/util/userConfigCgroup.php alice --apply --device=/home --io-profile=hdd # Reset to defaults /scripts/util/userConfigCgroup.php alice --apply --wipe
IO profiles:
hdd: IOWeight=200, read/write bandwidth throttles (5M/10M), IOPS throttles (100/100)nvme: IOWeight=200, no throttles (weights have limited effect on NVMe)bulk: Higher IOWeight (~500), CPUWeight (~300), TasksMax (~8192) for throughput
Process priority management
The root crontab applies ionice and renice rules every minute to ensure fair resource sharing:
| Process | ionice class | renice |
|---|---|---|
| rtorrent | class 2 (best-effort), priority 3 | -- |
| deluged | class 2, priority 3 | -- |
| qbittorrent-nox | class 2, priority 3 | -- |
| bash | class 1 (realtime), priority 2 | -- |
| php | class 2, priority 6 | -- |
| sftp-server | class 2, priority 7 | -- |
| proftpd | class 2, priority 7 | -- |
| rar, unrar, tar, cp, mktorrent | class 3 (idle) | -- |
| mono | -- | +17 |
| ffmpeg | class 3 (idle) | +18 |
| rclone | class 3 (idle) | +17 |
| xmrig | -- | +19 |
Systemd service hardening
systemdServicesGuard.php runs every minute and enforces that unwanted system-wide daemons stay stopped, disabled, and masked. This includes apache2, deluged, deluge-web, exim4, transmission-daemon, and redis-server. These services should only run as per-user instances, not system-wide.
SSH and FTP
PMSS configures sshd via /etc/seedbox/config/template.sshd_config and ProFTPD via /etc/seedbox/config/template.proftpd. ProFTPD is configured with passive ports 60000-65535 and a maximum of 16 connections per user/host.
Cron jobs and automation
PMSS installs a root crontab from /etc/seedbox/config/root.cron. This canonical template is applied on fresh hosts via crontab etc/seedbox/config/root.cron. On existing hosts, setupRootCron.php merges any missing entries without overwriting operator customizations.
The crontab drives the majority of PMSS's runtime behavior. Without it, watchdogs do not run, traffic is not accounted, and crashed services are not restarted. If the crontab is lost or corrupted, restoring it from root.cron recovers normal operation immediately.
The following tables list the cron jobs grouped by function. Intervals shown are the defaults from root.cron; operators can adjust timing to suit their hardware.
Service watchdogs
| Script | Interval | Description |
|---|---|---|
checkRtorrent.php |
2 min | Monitor rTorrent instances and restart as needed. Also checks for socket file. Kills duplicate instances. |
checkLighttpdInstances.php |
1 min | Confirm each user's lighttpd/php-cgi pair is running. Also checks HTTP 401 response. Restarts missing instances. |
checkDelugeInstances.php |
1 min | Start Deluge instances if enabled via ~/.delugeEnable. Logs actions.
|
checkQbittorrentInstances.php |
2 min | Restart qBittorrent if processes exit. |
checkRcloneInstances.php |
2 min | Maintain rclone mount processes. |
checkRootlessDocker.php |
5 min | Restart per-user Docker daemons when they exit. Logs to /var/log/pmss/rootlessDocker.log.
|
checkWireguard.php |
5 min | Ensure WireGuard kernel module is loaded and wg-quick@wg0 stays active.
|
checkGui.php |
10 min | Verify the management GUI index.php is intact. Copies a fresh one if corrupted (can happen when user exceeds quota). |
checkDirectories.php |
hourly + @reboot | Repair expected directory hierarchy if it drifts. |
systemdServicesGuard.php |
1 min | Enforce stop/disable/mask policy for unwanted system daemons. |
cgroupRootCheck.php |
2 hours + @reboot | Ensure root slice is unlimited. Fixes limits if misconfigured. |
Monitoring and statistics
| Script | Interval | Description |
|---|---|---|
trafficLog.php |
5 min | Capture per-user traffic counters from iptables |
trafficIngressLog.php |
5 min | Capture ingress traffic counters |
trafficStats.php |
30 min | Aggregate traffic logs into monthly statistics |
trafficIngressStats.php |
30 min | Aggregate ingress traffic logs |
trafficLimits.php |
hourly | Refresh per-user traffic throttling |
resourceLog.php |
5 min | Log per-user resource usage |
resourceStats.php |
30 min | Aggregate resource logs |
diskIostat.php |
5 min | Collect disk I/O metrics (iostat) |
cpuStat.php |
15 min | Record CPU usage statistics |
systemStatsLog.php |
30 min | System-wide statistics snapshot |
processSnapshot.php |
30 min | Process tree snapshots for postmortem analysis |
quotaSnapshot.php |
daily | Daily quota usage snapshot |
resourceSnapshot.php |
daily | Daily resource usage snapshot |
storageHealthSnapshot.php |
6 hours | Storage health data collection |
updateQuotas.php |
1 min | Refresh user disk quota information |
Maintenance
| Script | Interval | Description |
|---|---|---|
userTrackerCleaner.php |
2 hours | Remove known problematic public trackers from non-private torrents. Per-user opt-out via ~/.trackerCleanerDisable. Backs up originals before modification. Processes at most 500 torrents per user per run (see Tracker cleaner).
|
cleanupUserDb.php |
daily (02:30) | Prune stale entries from /etc/seedbox/config/users/*.json
|
certbotRenewalCheck.php |
6 hours | Check and renew Let's Encrypt certificates |
wireguardPeersRefresh.php |
6 min | Rebuild WireGuard config from user public keys |
backupEtc.sh |
1st and 15th of month | Snapshot /etc into timestamped archives
|
Reboot tasks
On system boot (@reboot):
checkDirectories.php-- Repair directory hierarchysetupNetwork.php(1s delay) -- Reapply firewall and traffic rulescheckLighttpdInstances.php(5s delay) -- Start user lighttpd processescheckRtorrent.php(10s delay) -- Start user rTorrent instancesmotdGenerate.php(15s delay) -- Regenerate message of the daycgroupRootCheck.php(20s delay) -- Ensure root cgroup is unlimitedsystemdServicesGuard.php(25s delay) -- Stop unwanted system daemons
RAID integrity checks
PMSS configures quarterly RAID integrity checks, staggered by hostname hash. This replaces Debian's default monthly check (which runs all servers simultaneously). The hostname hash determines both which months the check runs (one of three groups) and the sleep offset (0-5 hours) for start time spread.
Configuration
Config directory
/etc/seedbox/config/ contains all PMSS configuration and templates:
Template files
| Template | Generates |
|---|---|
template.rtorrent.rc |
rTorrent configuration with placeholder tokens for memory, ports, directories |
template.lighttpd |
Per-user lighttpd config including PHP FastCGI, ruTorrent, rclone, qBittorrent, Deluge proxy, public web directory, rate limiting, streaming |
template.nginx-conf |
Global nginx.conf (keepalive, body size, TLS) |
template.nginx-site-default |
Default nginx site config |
template.nginx-site-default-ssl |
SSL variant for self-signed cert |
template.nginx-site-default-ssl-lets-encrypt |
SSL variant for Let's Encrypt |
template.nginx-user |
Per-user nginx reverse proxy config (proxies to user lighttpd) |
template.nginx-user-suspended |
Suspended user nginx config (serves suspension notice) |
template.proftpd |
ProFTPD config (passive ports, connection limits) |
template.sshd_config |
SSH daemon config |
template.deluge.* |
Deluge auth, core, hostlist, and web configs |
template.qbittorrent.conf |
qBittorrent configuration with randomized external port |
template.fireqos |
FireQOS traffic shaping rules |
template.qos |
QoS configuration |
template.openvpn.* |
OpenVPN server and client configs |
template.wireguard.* |
WireGuard server config and user readme |
template.rc.local |
System rc.local for boot-time tuning |
template.systemd.* |
Systemd service files (boot tuning, services guard) |
template.cgroup.user-slice.v1.conf |
Cgroup v1 user slice template |
template.cgroup.user-slice.v2.conf |
Cgroup v2 user slice template |
template.motd |
Message of the day template |
template.logrotate.pmss |
Log rotation policy for PMSS logs |
template.rsyslog-remote.conf |
Remote syslog forwarding template |
template.sources.buster |
APT sources for Debian 10 |
template.sources.bullseye |
APT sources for Debian 11 |
template.sources.bookworm |
APT sources for Debian 12 |
template.sources.trixie |
APT sources for Debian 13 (experimental) |
template.sources.jessie |
APT sources for Debian 8 (legacy) |
template.rutorrent.config |
ruTorrent configuration (SCGI socket, paths, timeouts) |
template.rutorrent.access |
ruTorrent access control |
Runtime configuration files
| File | Description |
|---|---|
network |
Network interface name and link speeds (e.g., eth0 and 1Gbit)
|
localnet |
Local network ranges for separate traffic tracking (one CIDR per line) |
cgroup.policy.php |
Per-host cgroup policy overrides |
logging.conf |
Optional remote syslog forwarding configuration |
version |
Current PMSS version spec and timestamp |
version.meta |
Resolved branch, commit, and log destinations (JSON) |
root.cron |
Canonical root crontab template |
rtorrent.resources.json |
rTorrent resource allocation settings |
user.crontab.default |
Default per-user crontab |
user.rtorrent.defaults.dht |
Default DHT settings for user rTorrent |
user.rtorrent.defaults.pex |
Default PEX settings for user rTorrent |
api.* |
API configuration files (debug, keys, server) |
User skeleton
New user accounts are provisioned from /etc/skel/:
www/-- Web GUI (index.php, info.php, stats.php, welcome.php, ruTorrent, file manager, deluge/qbittorrent/rclone pages)www/rutorrent/-- ruTorrent installation with themes and pluginsbin/-- User scripts (docker-install-wireguard.sh)install-media-stack.sh-- Self-contained media stack installerdata/-- Default download directorywatch/-- Watch directory for automatic torrent loading
Logging
All PMSS cron jobs and operations log to /var/log/pmss/. Log rotation is configured via /etc/logrotate.d/pmss-update (7 daily copies, compressed, copytruncate).
Log files of note:
| Log file | Source |
|---|---|
/var/log/pmss-update.log |
Phase 2 update (plaintext) |
/var/log/pmss-update.jsonl |
Update events (JSON, one object per step) |
/var/log/pmss/update.log |
Phase 1 bootstrap log |
/var/log/pmss/addUser.log |
User provisioning |
/var/log/pmss/users.log |
Per-user operation log |
/var/log/pmss/users.jsonl |
Per-user events (JSON) |
/var/log/pmss/users/<user>.log |
Individual user log |
/var/log/pmss/checkRtorrent.log |
rTorrent watchdog |
/var/log/pmss/checkLighttpdInstances.log |
Lighttpd watchdog |
/var/log/pmss/rootlessDocker.log |
Docker watchdog |
/var/log/pmss/trafficLog.log |
Traffic data collection |
/var/log/pmss/trafficLimits.log |
Traffic limit enforcement |
/var/log/pmss/setupNetwork.log |
Network/firewall setup |
/var/log/pmss/system-stats.log |
System statistics |
/var/log/pmss/process-snapshot.log |
Process snapshots |
/var/log/pmss/quota-daily.log |
Daily quota snapshots |
/var/log/pmss-install.log |
Initial installation |
Optional remote log forwarding via rsyslog is supported. Create /etc/seedbox/config/logging.conf with remote_logging_enabled=1, remote_host, and optionally remote_port and remote_protocol.
Structured logging (JSONL)
The update system writes structured JSON events to /var/log/pmss-update.jsonl, one JSON object per line. Each event includes a timestamp, step name, duration, and outcome. This format is machine-parseable and suitable for log aggregation tools.
Example events from a typical update:
{"ts":"2026-02-14T04:31:12+00:00","step":"apt-flush","duration_ms":42310,"result":"ok"}
{"ts":"2026-02-14T04:31:55+00:00","step":"app:rtorrent","duration_ms":18200,"result":"ok"}
{"ts":"2026-02-14T04:32:14+00:00","step":"user-env-refresh","users":26,"duration_ms":8400,"result":"ok"}
Profiling data can be captured with --profile-output=/path/to/file.json for detailed runtime breakdowns of each update step. This is useful for identifying slow installer modules or package operations that stall.
Per-user operations also produce structured logs. /var/log/pmss/users.jsonl records user provisioning, suspension, termination, and configuration changes with the same one-object-per-line format.
Observability probes
Two CLI tools provide read-only system health checks that can be called from monitoring systems or CI pipelines:
# System test — checks binary versions, config presence, readiness /scripts/util/systemTest.php /scripts/util/systemTest.php --json # Component status — reports per-component health /scripts/util/componentStatus.php /scripts/util/componentStatus.php --json
Both support --json output for integration with monitoring dashboards. The probes are read-only and safe to run on production hosts at any time.
The version metadata stored in /etc/seedbox/config/version records the current deployment spec and timestamp (e.g., git/main:2025-01-01@2025-01-02 03:04). The companion version.meta file stores the resolved branch, commit hash, and log destinations in JSON format.
Tracker cleaner
PMSS includes a tracker cleaner that removes known problematic public trackers from non-private torrents to prevent rTorrent stalls on shared hosts. It has been in use since November 2020.
How it works:
- Only non-private torrents are processed. Torrents with the BitTorrent
privateflag are skipped entirely. - A timestamped backup of every original
.torrentis written before any change. - Scans at most 500 torrents per run, 2 users per pass.
- Stops after 30 minutes or 500 modified torrents.
- Per-user opt-out: create
~/.trackerCleanerDisable. - Change log:
~/.trackerCleaner.logand~/.logs/trackerCleaner.log. - Backups:
~/session/backups/YYYY-mm-dd_HHMM/.
The removal list targets specific known-problematic tracker URLs and domains (e.g., tracker.openbittorrent.com, tracker.leechers-paradise.org, tracker.coppersurfer.tk, rarbg.*). The full list is documented in docs/tracker-cleaner.md.
Port management
portManager.php manages HTTP server port assignments for user services:
/scripts/util/portManager.php assign alice lighttpd /scripts/util/portManager.php view alice lighttpd /scripts/util/portManager.php release alice lighttpd
Port information is stored under /etc/seedbox/runtime/ports using files named SERVICE-USER. Assignments are guarded by a per-service lock to avoid concurrent collisions.
Hardware transcoding
PMSS documents hardware-accelerated video transcoding for FFmpeg and Jellyfin in docs/hardware-transcoding.md. Intel iGPUs with VAAPI are the best-supported option. Users must be in the render group to access /dev/dri/renderD128.
FFmpeg can be installed entirely in userspace. Static builds can be placed in ~/.bin/ffmpeg. The installer flag --jellyfin-ffmpeg=/home/<user>/.bin/ffmpeg stamps the path in Jellyfin configuration.
System requirements
Operating system
- Debian 10 (buster) -- stable, in production
- Debian 11 (bullseye) -- in production
- Debian 12 (bookworm) -- in production
- Debian 13 (trixie) -- experimental
Hardware
- At least 2 GiB free disk space on the root partition for PMSS itself
- Dedicated
/homepartition for user data storage (ext4 recommended, with quota support) - Network interface (1 Gbps or 10 Gbps)
- Optional: Intel iGPU for hardware transcoding (VAAPI)
Prerequisites
The installer automatically installs these packages:
- PHP CLI with xml, zip, mbstring extensions
- git, rsync, curl, wget, ca-certificates, unzip, vim
- Build toolchain: build-essential, autoconf, automake, pkg-config, libtool, subversion
- nano, quota, tzdata
Development
Repository structure
The source code is at https://github.com/MagnaCapax/PMSS.
scripts/-- Main PHP scripts, deployed to/scriptson production hostsetc/-- Configuration templates and skeleton files, deployed to/etcvar/-- Web content and default pages, deployed to/vardocs/-- Documentation (architecture, update workflow, cron, cgroups, Docker, WireGuard, security, etc.)docs/adr/-- Architecture Decision Recordstests/-- Test TODO trackertools/-- Development tools (phploc, CI scripts, prompts)development/-- Agent development tooling
Testing
PMSS has two test tiers:
Development tests (php scripts/lib/tests/development/Runner.php):
- 143 test files covering update spec parsing, cgroup configuration, distro detection, package selection, WireGuard installation, config validation, traffic accounting, resource limit calculation, and many other subsystems
- Hermetic: no network access, no system mutations, no root required
- Tests create temporary files and override environment variables to simulate different OS releases, network configurations, and edge cases (e.g.,
PMSS_OS_RELEASE_PATHto simulate Debian 13 on a Debian 12 development machine,PMSS_WG_DNS_IPto test WireGuard with different DNS servers) - Tests can run on any developer machine with PHP CLI — no PMSS installation needed
Production tests (php scripts/lib/tests/production/Runner.php):
- Read-only probes for live hosts after provisioning
- Validate package presence, service status, and configuration
Testing scripts
scripts/testing/ provides orchestration scripts:
test-php.sh-- PHP syntax lint and development suitetest-bash.sh-- Bash syntax check and optional lint/formattest-all.sh-- Runs all testsdoctrine-lint.sh-- ADR guardrailssharp-edges-lint.sh-- Flags dangerous patterns like rawrm -rfoutside wrappersnet-edges-lint.sh-- Flags raw network calls outside wrappersphpstan.sh-- Static analysis (opt-in viaPMSS_LINT_PHPSTAN=1)
CI
GitHub Actions run PHP lint, development tests, and bash checks on pushes and pull requests. Configuration is in .github/workflows/ci.yml.
Design principles
Several engineering decisions run through the PMSS codebase:
Never break old users. Backwards compatibility is mandatory. When a configuration format changes or a new application replaces an old one, migration code handles the transition automatically during the next update. Users do not need to take any action. If 100% compatibility cannot be ensured, intermediary migration code bridges the gap.
Compile from source when it matters. rTorrent and libtorrent are compiled from source (along with xmlrpc-c) rather than installed from Debian packages. This allows PMSS to ship a consistent, tested version across all Debian releases and apply performance patches. The tradeoff is longer initial install time, but the compiled binaries are cached and only rebuilt when the target version changes.
Idempotent everything. Running update.php twice produces the same result as running it once. The same applies to user provisioning, config generation, and network setup. This makes it safe to re-run the updater after a partial failure without worrying about duplicate state.
Watchdog over daemon. Rather than running a management daemon, PMSS uses cron-based watchdogs that check and repair state on fixed intervals. If rTorrent crashes, checkRtorrent.php restarts it within 2 minutes. If lighttpd dies, checkLighttpdInstances.php catches it within 1 minute. Watchdogs are simpler than daemons: they have no state to corrupt, no sockets to manage, and no crash recovery of their own to worry about.
PHP for management, not for serving. PMSS management scripts are written in PHP because that is what the project started with and it works. PHP CLI has no external dependencies beyond the packages Debian ships. The web-facing components (ruTorrent, file manager, stats pages) also happen to be PHP, but the management layer does not depend on a web server.
Per-user everything. Each user has their own lighttpd, their own torrent client, their own Docker daemon, their own cgroup slice, and their own PHP process. No shared server processes handle multiple users. This isolation model means a misbehaving user cannot affect other users' web interfaces or torrent clients.
Contributing
Contributions are welcome regardless of size. Guidelines from CONTRIBUTING.md:
- Discuss significant changes via issue or ADR before implementation
- Keep commits small and focused
- Update code, tests, docs, and ADRs together
- Validate locally before submitting a PR
- Never break old users. Backwards compatibility is mandatory. Intermediary migration code is required if 100% compatibility cannot be ensured.
Code style follows Linux kernel conventions adapted for PHP:
- camelCase for variables, functions, and classes
- Line width ~120-160 characters
- Maximum 4 nesting levels per source file
- Single source files target ~150 lines
- Descriptive function and variable names with comments on purpose
Contributions may be rewarded with Pulsed Media service credit.
Architecture Decision Records
Significant technical decisions are recorded as ADRs in docs/adr/:
- ADR-0003: Cgroup v2 adoption and dual-path detection
- ADR-0004: Shell command guardrails
- ADR-0005: Trust boundaries for internal tools
- ADR-0006: Update-step2 user loop and observability
- ADR-0007: Install bootstrap interactivity and contract
- ADR-0008: Reject deb822 APT sources migration
- ADR-0009: Nginx lightweight reverse proxy
CLI probes
Two CLI tools provide read-only system health checks:
/scripts/util/systemTest.php-- Checks binary versions, config presence, system readiness. Supports--jsonoutput./scripts/util/componentStatus.php-- Reports component status. Supports--jsonoutput.
Documentation
In-repository documentation
The docs/ directory contains:
| File | Topic |
|---|---|
architecture.md |
System architecture overview, bootstrap flow, key modules |
install.md |
Installer contract and behavior details |
update.md |
Two-phase update workflow, app installer matrix, execution outline |
cron.md |
Cron automation, script catalogue, scheduling tips |
cgroup.md |
Cgroup resource control, per-user utility, policy overrides |
userConfig.md |
Adjusting user settings |
addUser.md |
Adding new users |
wireguard.md |
WireGuard VPN setup, tenant quick start |
docker-help.md |
Rootless Docker basics |
linuxserver.io.md |
Running LinuxServer.io containers on PMSS |
install-media-stack.md |
Native media stack installer documentation |
hardware-transcoding.md |
FFmpeg and Jellyfin hardware transcoding guide |
logging.md |
Central log shipping via rsyslog |
tracker-cleaner.md |
Tracker cleaner documentation |
setupNetwork.md |
Network and firewall configuration |
setupLetsEncrypt.md |
Let's Encrypt certificate setup |
portManager.md |
Port management utility |
contracts.md |
Function and script behavioral contracts |
testing.md |
Testing orchestration and CI |
maintenance.md |
Maintenance checklist |
recovery.md |
Recovery procedures for failed upgrades |
dpkg-baseline.md |
Package baseline capture workflow |
security/operational-safety.md |
Multi-tenant operational safety practices |
security/testing.md |
Security testing documentation |
Wiki documentation
- Installing PM Software Stack -- Installation guide
- Category:PM Software Stack Guides -- User guides for common tasks
- Adding a new user -- User provisioning guide
- Changing user settings -- User reconfiguration guide
- Removing user -- User termination guide
- PMSS Suspending/Unsuspending user -- Suspension management
Changelog
Current development is tracked on GitHub at https://github.com/MagnaCapax/PMSS/commits/main.
Historical changelog entries (2011-2023) from before the GitHub migration are preserved at PM Software Stack Changelog Archive.
See also
- Seedbox
- Pulsed Media
- Pulsed Media Datacenters
- RAID
- Installing PM Software Stack
- Category:PM Software Stack Guides
- PM Software Stack Changelog Archive
- PM Software Stack Changelog 2011-2014
On the blog: