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

ItemLinuxWindows
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.shMachine 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, bare pip uses the venv binary and skips PMG shims. Call pmg 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 real npm/pip ahead of PMG shims even when the shim directory is on PATH. Prefer putting /usr/local/lib/pmg/bin first in a durable ENV PATH / login PATH, or call pmg npm / pmg pip explicitly. pmg setup doctor warns for any supported package manager that resolves outside the shim directory.
  • No shell aliases. System install only installs PATH shims. There is no pmg.rc alias layer.
  • Config changes. pmg config set and pmg config edit are unavailable while the system config is active. Edit /etc/safedep/pmg/config.yml as 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 sets global_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/bin as root:staff mode 2775), a group member can delete the root-owned binary and replace it, bypassing the check. staff is empty by default, so default exposure is nil; on a multi-user host where the group is not trusted, run sudo chmod g-w /usr/local/bin or install into a root:root directory.
  • Elevation only, not impersonation. Root's per-user data is diverted to /root only for sudo to root (detected via SUDO_USER). su without - 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 next pmg run. sudo -u <user> runs with only that user's rights, so it cannot poison another account at all, it just fails. Prefer sudo or su -, or set PMG_CONFIG_DIR.

User data directories

Shared policy lives under /etc/safedep/pmg. Runtime data stays per user:

DataDefault 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 (su without -, images that set ENV HOME before dropping root, or an older pmg under sudo). Restore ownership: sudo chown -R $(id -un) ~/.config/safedep
  • Inside another user's home: your environment leaked that user's HOME or XDG_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.