System Install
September 12, 2026 · View on GitHub
Use system install when one machine or image should protect every user account: shared VMs, golden Docker images, MDM fleets, and similar setups. It runs on Linux and Windows.
sudo pmg setup install --system
On Windows, run the same command from a terminal started as administrator:
pmg setup install --system
Requires root on Linux and an administrator on Windows. Install PMG into a standard system path first: /usr/local/bin on Linux, %ProgramFiles%\safedep\pmg\pmg.exe on Windows. A user-local build (e.g. ~/go/bin/pmg, or pmg.exe from npm install -g) is rejected.
--system checks the binary because every user's shim runs it by absolute path. On Linux, root must own the binary and its directory, only root may write to them, and every user must be able to execute the binary. On Windows, the binary must be at the path above, with no link or junction in that path. The install then sets one security descriptor on each object it owns: Administrators as owner, full control for SYSTEM and Administrators, read and execute for Users, no inheritance. pmg setup doctor reports an object whose descriptor differs. A second pmg setup install --system restores it.
Per-user pmg setup install remains available and does not conflict with a system install.
To uninstall:
sudo pmg setup remove --system
sudo pmg setup remove --system --config-file # also remove the system config file
On Windows, run the same two commands without sudo, from a terminal started as administrator.
Files created
| Item | Linux | Windows |
|---|---|---|
| Configuration | /etc/safedep/pmg/config.yml | %PROGRAMDATA%\safedep\pmg\config.yml |
| Package-manager shims | /usr/local/lib/pmg/bin | %ProgramFiles%\safedep\pmg\bin\*.cmd |
| PATH | /etc/profile.d/pmg.sh | Machine PATH: shim directory first, pmg.exe directory appended |
Making shims visible on PATH
Processes only use the shims when the shim directory is on PATH ahead of the real npm, pip, and other package managers. On Linux that directory is /usr/local/lib/pmg/bin.
Windows
pmg setup install --system puts %ProgramFiles%\safedep\pmg\bin first on the machine PATH. Windows builds the process PATH from the machine value, then the user value. The shims are therefore ahead of npm from the Node.js MSI and other machine-wide installers. The install also appends the directory of pmg.exe, so pmg resolves in every terminal. Open a new terminal after the install. pmg setup remove --system deletes the shim entry and keeps the pmg.exe entry.
A per-user install cannot do this. A user PATH entry never comes before a machine entry. A user-writable directory must not be on the machine PATH, because an elevated process could run a binary that a standard user put there.
Confirm with:
Get-Command npm # should resolve under %ProgramFiles%\safedep\pmg\bin
pmg setup doctor
Linux VMs and login shells
pmg setup install --system installs /etc/profile.d/pmg.sh, which prepends the shim directory for login shells.
sudo pmg setup install --system
New login sessions pick this up automatically. For an already open shell, start a new login session or run:
source /etc/profile.d/pmg.sh
Confirm with:
which npm # should resolve under /usr/local/lib/pmg/bin
pmg setup doctor
Docker and container images
Docker RUN does not load /etc/profile.d. After system install you must set ENV PATH so build steps and the runtime container see the shims:
FROM node:22-bookworm
RUN curl -fsSL https://raw.githubusercontent.com/safedep/pmg/main/install.sh | sh \
&& pmg setup install --system
# Required: profile.d is not sourced during docker build
ENV PATH="/usr/local/lib/pmg/bin:$PATH"
# Optional: switch user; PATH from ENV still applies
RUN mkdir -p /app && chown node:node /app
WORKDIR /app
USER node
COPY --chown=node:node package*.json ./
RUN npm ci
Derived images inherit that ENV. Later RUN npm install / RUN pip install go through PMG for any USER.
If a child Dockerfile sets ENV PATH=... again, keep /usr/local/lib/pmg/bin ahead of the real npm/pip directories. Leaving it out (or behind those toolchains) drops interception.
PMG running on the Docker host cannot inspect package installations inside docker build. PMG must be installed in the image as shown above.
Configuration
The system config file is authoritative for every user. A per-user config.yml is ignored while /etc/safedep/pmg/config.yml exists. On Windows the file is %PROGRAMDATA%\safedep\pmg\config.yml.
pmg config set and pmg config edit fail under a system config. Update the file as root, or as an administrator on Windows, or redeploy it through your image or configuration management.
On Windows, the install sets the same security descriptor on %PROGRAMDATA%\safedep, %PROGRAMDATA%\safedep\pmg and the config file. A config.yml that already exists there is kept only when it carries that descriptor. Any other file stops the install with a message that names it. Inspect the file, delete it, and run the install again. For an MDM deployment, run pmg setup install --system first, then edit the file it writes.
At run time, PMG obeys the managed config only when Administrators or SYSTEM own the file and no other account can write it. Otherwise PMG ignores the file and prints a warning. pmg setup doctor reports a managed config whose descriptor differs from the one the install wrote.
Optional lockdown (global_lockdown: true) is documented in config.md.
For a Windows MDM fleet, the scripts in scripts/mdm run the system install and deploy a managed config. The Linux scripts deploy a managed config and a per-user setup.
Limitations
- Virtualenv. After
source .venv/bin/activate, barepipuses the venv binary and skips PMG shims. Callpmg pip …explicitly. - Version managers. Tools like nvm, pyenv, volta, and asdf often prepend their own bin directories from shell rc files that run after
/etc/profile.d. That can put realnpm/pipahead of PMG shims even when the shim directory is onPATH. Prefer putting/usr/local/lib/pmg/binfirst in a durableENV PATH/ login PATH, or callpmg npm/pmg pipexplicitly.pmg setup doctorwarns for any supported package manager that resolves outside the shim directory. - No shell aliases. System install only installs PATH shims. There is no
pmg.rcalias layer. - Config changes.
pmg config setandpmg config editare unavailable while the system config is active. Edit/etc/safedep/pmg/config.ymlas root, or redeploy the file. - Custom sandbox
policy_templates. Relative paths in the system config resolve under each user's config directory, not/etc/safedep/pmg. Prefer absolute paths. pmg sandbox allow. Blocked when the system config setsglobal_lockdown: true.- Group-writable install directory. The binary must be root-owned and non-writable, but if its directory is group-writable without the sticky bit (Debian/Ubuntu ship
/usr/local/binasroot:staffmode2775), a group member can delete the root-owned binary and replace it, bypassing the check.staffis empty by default, so default exposure is nil; on a multi-user host where the group is not trusted, runsudo chmod g-w /usr/local/binor install into aroot:rootdirectory. - Elevation only, not impersonation. Root's per-user data is diverted to
/rootonly forsudoto root (detected viaSUDO_USER).suwithout-becomes root with no marker, so it can still create root-owned files in the caller's home; the caller sees a clear error and chown fix on their nextpmgrun.sudo -u <user>runs with only that user's rights, so it cannot poison another account at all, it just fails. Prefersudoorsu -, or setPMG_CONFIG_DIR.
User data directories
Shared policy lives under /etc/safedep/pmg. Runtime data stays per user:
| Data | Default location |
|---|---|
| Event logs | ~/.config/safedep/pmg/logs/ |
| Cloud sync state | ~/.config/safedep/pmg/cloud-sync.db |
| Cache | ~/.cache/safedep/pmg/ |
| Sandbox overlays | ~/.config/safedep/pmg/sandbox/overlays/ |
| Persistent CA keypair | ~/.config/safedep/pmg/ca-cert.pem, ca-key.pem |
You can relocate these with PMG_CONFIG_DIR and PMG_CACHE_DIR.
When pmg runs under sudo (a non-root user elevated to root), its per-user data resolves under root's own home (/root), not the invoking user's, even if sudo preserved their HOME. Running directly as root honors HOME/XDG_CONFIG_HOME as usual, so golden images that set those on purpose keep working. This detection relies on sudo's SUDO_USER marker: su without - leaks the caller's environment but leaves no marker, so a root shell obtained that way can still write into the caller's home. Prefer su - or sudo.
The invoking user must be able to write their config directory. PMG records an event log there on each run and fails the command if it cannot (unless event logging is disabled in config).
In Docker images, avoid creating /home/<user>/.config/safedep as root during the build. Either fix ownership for the runtime user, or set PMG_CONFIG_DIR to a writable location.
If every pmg command fails with permission denied on the event log, check where the reported path points:
- Inside your own home: a root run created it as root (
suwithout-, images that setENV HOMEbefore dropping root, or an older pmg undersudo). Restore ownership:sudo chown -R $(id -un) ~/.config/safedep - Inside another user's home: your environment leaked that user's
HOMEorXDG_CONFIG_HOME(e.g.sudo -u <user>on GitHub-hosted runners). Fix the environment (export XDG_CONFIG_HOME="$HOME/.config"). Do not chown another user's directory; that bricks their pmg instead.
The error message and pmg setup doctor print the fix matching your case.
For cloud sync, enable cloud in the system config and provide credentials (SAFEDEP_API_KEY and SAFEDEP_TENANT_ID, or a keychain login on developer machines).
For MDM fleets, also schedule pmg cloud sync hourly (launchd, systemd timer, or your MDM's script scheduler). Each sync also sends a rate-limited check-in, so an idle endpoint still appears as active in the SafeDep Cloud Endpoints view.
Certificates
System install does not set up a MITM certificate authority. For npm and pip on Linux, PMG's default ephemeral CA and environment-variable injection are enough.
To install a persistent CA into the OS trust store, use a separate command:
pmg setup cert install --system
Run that as your normal user. Details are in cert.md.