systemd-creds
Overview
systemd-creds lists, shows, encrypts, and decrypts system and service credentials — small secrets and parameters passed into units as files under $CREDENTIALS_DIRECTORY, not as inherited environment variables. Encrypted credentials use AES256-GCM with a key derived from the TPM2, a host secret under /var/lib/systemd/credential.secret, or both (default when TPM and persistent /var exist).
Companion unit settings (see systemd.exec(5)): LoadCredential=, LoadCredentialEncrypted=, SetCredential=, SetCredentialEncrypted=, ImportCredential=.
Syntax
systemd-creds [OPTIONS] list
systemd-creds [OPTIONS] cat NAME
systemd-creds encrypt [OPTIONS] INPUT|- OUTPUT|-
systemd-creds decrypt [OPTIONS] INPUT|- OUTPUT|-
systemd-creds setup # ensure host credential secret exists (as needed)Common options:
| Option | Meaning |
|---|---|
--system |
Operate on system credentials (PID1), not the current service context |
--name=NAME |
Embed/override credential name in the encrypted blob |
-p / --pretty |
With encrypt and stdout: print a SetCredentialEncrypted= line for unit files |
--with-key=host\|tpm2\|auto\|… |
Select encryption key backend (see man page; auto is typical) |
-H / -T |
Shortcuts affecting host/TPM key use (see systemd-creds(1)) |
Safety
- Plaintext files used as
encryptinput should beshred -u(or equivalent) after producing ciphertext. SetCredential=in unit files is world-readable — never put raw secrets there; useSetCredentialEncrypted=orLoadCredentialEncrypted=.- Decryption needs the same machine key material (TPM +/or
/varsecret). Ciphertext is not portable across hosts by default. - Credentials are limited (~1 MiB per service aggregate). Not a general secret store for huge blobs.
- Prefer
PrivateMounts=/ sandboxing so$CREDENTIALS_DIRECTORYis not visible to other services.
Examples with Explanations
Encrypt a file and load it in a transient service
echo -n 'supersecret' > /tmp/db.pass
systemd-creds encrypt --name=dbpass /tmp/db.pass /etc/credstore.encrypted/dbpass.cred
shred -u /tmp/db.pass
sudo systemd-run -P --wait \
-p LoadCredentialEncrypted=dbpass:/etc/credstore.encrypted/dbpass.cred \
systemd-creds cat dbpass
# prints: supersecretLoadCredentialEncrypted= decrypts at activation and exposes plaintext only as $CREDENTIALS_DIRECTORY/dbpass inside the service.
Emit SetCredentialEncrypted= for a unit file
systemd-ask-password -n | systemd-creds encrypt --name=mysql-password -p - -
# Paste the printed SetCredentialEncrypted=mysql-password: \ … block into the unit [Service] sectionRelative names and credstore search paths
With a relative source, systemd searches system credentials, then /etc/credstore/, /run/credstore/, /usr/lib/credstore/ (and *.encrypted variants for encrypted load/import):
sudo mkdir -p /etc/credstore.encrypted
sudo systemd-creds encrypt --name=api-token - /etc/credstore.encrypted/api-token.cred <<'EOF'
tok_desk_example
EOF
# Unit snippet:
# [Service]
# LoadCredentialEncrypted=api-token
# ExecStart=/usr/local/bin/app --token-file=${CREDENTIALS_DIRECTORY}/api-tokenList / show in context
# From an interactive shell (usually empty):
systemd-creds list
# System credentials passed into the machine:
systemd-creds --system list
systemd-creds --system cat some-nameApplication read pattern
# Inside the service (ExecStart script):
# test -n "$CREDENTIALS_DIRECTORY"
# install -m 0400 "$CREDENTIALS_DIRECTORY/dbpass" /run/app/db.pass
# Or point the daemon at %d/dbpass via Environment= or ExecStart=In unit files, %d expands to the credential directory — useful for Environment=TOKEN_FILE=%d/api-token without hardcoding /run/credentials/….
TPM / host key notes
# Default encrypt binds to local TPM2 + host secret when available
systemd-creds encrypt --name=backup ./plaintext ./plaintext.cred
# Initrd/generator consumers may need TPM-only wrapping so /var is not required yet:
systemd-creds encrypt --with-key=auto-initrd --name=early ./plaintext ./plaintext.credExact --with-key= values evolve with systemd releases — check systemd-creds encrypt --help on the target host.
Try this
- Encrypt a one-line secret to
/tmp/demo.cred, runsystemd-runwithLoadCredentialEncrypted=, andcatit viasystemd-creds. - Generate a
SetCredentialEncrypted=line with-pand drop it into a throwaway unit under/etc/systemd/system/. - Compare
systemd-creds encrypt --helpkey backends on a machine with and without TPM2. - Confirm a non-root service user can read
$CREDENTIALS_DIRECTORY/NAMEbut another user cannot.