Put a password straight into a unit file, and anyone who can read that file has the secret: systemd-creds exists so you never have to. You will create an encrypted credential, decrypt it to verify the round trip, and pass it to a service without the plaintext ever touching the unit file. Allow 15 minutes for a disposable test, or longer if you are adding a credential to a real service. The commands below describe the installed systemd 255 package.
A credential is a small file made available to a unit through $CREDENTIALS_DIRECTORY. The service reads that file at run time. The unit configuration can contain encrypted data or a reference to an encrypted file, while the plaintext stays out of the unit file.
Start by confirming the version and the security hardware status. These commands do not change configuration and do not require elevated privileges:
$ systemd-creds --version
systemd 255 (255.4-1ubuntu8.17)
$ systemd-creds has-tpm2
partial
-firmware
-driver
+system
+subsystem
-libraries
The exact package suffix and the TPM2 detail lines vary. A result of yes means firmware, drivers, the kernel and systemd all support a usable TPM2 device. no means it is not available. partial is still a failure status for scripts, so test the exit status rather than matching only the printed word:
$ systemd-creds has-tpm2 --quiet
$ printf 'status: %s\n' "$?"
status: 19
This machine's status is partial, so do not assume a TPM-bound example will work here. The default --with-key=auto choice uses TPM2 when suitable and can also use the host key when /var/lib/systemd/ is on persistent storage. The resulting credential may require both the original TPM2 device and the original operating-system installation.
For a harmless test, make a temporary plaintext and restrict its permissions before writing anything encrypted. Use a real secret only after you have checked the key choice. This example uses a visibly fake value:
$ umask 077
$ printf '%s' 'demo-secret-change-me' > /tmp/demo-secret.plain
$ chmod 600 /tmp/demo-secret.plain
Do not put a production password directly in a command line: it can enter shell history or be visible briefly in process inspection. For an interactive value, use systemd-ask-password -n in the encryption pipeline shown later.
Encrypt the plaintext to a file whose final path component is the credential name. The name is embedded in the ciphertext. This prevents an encrypted credential being silently renamed and reused for another purpose.
$ systemd-creds encrypt /tmp/demo-secret.plain /tmp/demo-secret
The command prints no success message. A zero exit status is the first checkpoint. Inspect the result without displaying its contents as a terminal secret:
$ test -s /tmp/demo-secret && echo 'encrypted credential exists'
encrypted credential exists
$ file /tmp/demo-secret
/tmp/demo-secret: ASCII text
Encrypted credentials are Base64 text, but Base64 is only an encoding. The confidentiality and authentication come from the selected key and AES256-GCM, not from the visible text format. Treat this file as sensitive.
Decrypt to a new file and compare it with the original. Because the input filename is demo-secret, the embedded name matches automatically:
$ systemd-creds decrypt /tmp/demo-secret /tmp/demo-secret.recovered
$ cmp --silent /tmp/demo-secret.plain /tmp/demo-secret.recovered
$ printf 'round trip status: %s\n' "$?"
round trip status: 0
The output path may be omitted, in which case plaintext goes to standard output. That is useful for a pipeline, but it is also an easy way to leak a secret into a terminal log. Use a file and cmp when validating. Remove only these disposable test files after inspection; do not blindly delete a production credential that a unit still needs.
Renaming the encrypted file changes the name systemd-creds expects. This deliberate failure is useful when diagnosing a real setup:
$ cp /tmp/demo-secret /tmp/renamed-secret
$ systemd-creds decrypt /tmp/renamed-secret
Embedded credential name 'demo-secret' does not match filename 'renamed-secret', refusing.
Keep the filename and embedded name aligned, or provide the intended name explicitly with --name=demo-secret when using standard input, standard output or a deliberately different path. An empty name disables this validation and removes a useful protection, so do not use --name= casually.
For a credential that belongs directly in a unit drop-in, ask for the secret interactively and request the formatted setting. This command needs root privileges only when writing the resulting drop-in, not while asking the question or generating the line:
$ systemd-ask-password -n | systemd-creds encrypt --name=db-password --pretty - -
SetCredentialEncrypted=db-password: ...
The full output is a long encrypted value. Paste the complete line under a [Service] section, not the shortened display above. A safer file installation pattern is:
# mkdir -p /etc/systemd/system/example.service.d
# systemd-ask-password -n | (printf '%s\n' '[Service]' && systemd-creds encrypt --name=db-password --pretty - -) > /etc/systemd/system/example.service.d/50-db-password.conf
# chmod 600 /etc/systemd/system/example.service.d/50-db-password.conf
# systemctl daemon-reload
# systemctl restart example.service
These commands change persistent service configuration and restart the service. Confirm the unit name, keep a copy of the previous drop-in, and use a maintenance window. To undo this specific change, remove 50-db-password.conf, run systemctl daemon-reload, and restart the service again. Removing it is irreversible unless you retained the old file.
Leave --with-key=auto for the normal host service case when its hardware and storage assumptions are acceptable. Use --with-key=host when the credential must be decryptable from the host key and the host key is available. Use --with-key=tpm2 when access must depend on the original TPM2 device. Credentials intended for an initrd generally need --with-key=auto-initrd, because the host key is usually unavailable there.
Never treat --with-key=tpm2-absent as encryption for a secret. It uses a zero-length key and provides no confidentiality or authenticity. It exists for generation on systems without TPM2 support and is appropriate only when that limitation is intentional and understood. If a command fails under auto, check has-tpm2, persistent storage and the error before weakening the key mode.
cmp round trip.