RPM-SCRIPTLETS(7)
π¦ rpm-scriptlets(7) π¦
Miscellaneous Information Manual β¨
πΈ NAME πΈ
rpm-scriptlets β RPM transaction scriptlets
β¨ SYNOPSIS β¨
%SCRIPT [options] [NAME]
[body]
%TRIGGER [options] [NAME] -- TRIGGERCOND ...
[body]
%FILETRIGGER [options] [filetrigger-options] [NAME] -- PATHPREFIX ...
[body]
π DESCRIPTION π
okay so bestie, RPM is out here running arbitrary programs at all these pre-determined moments in a package's whole lifetime β like after installation, right before uninstall, or when another package gets installed or updated in a transaction. Those little programs are called scriptlets, and the spots where they run are called slots. π«Ά
There are three main types, and you're gonna want to know them:
- Scripts β invoked unconditionally at their predetermined slots in a transaction. no conditions, they just go.
- Triggers β invoked conditionally when their specified trigger condition matches other packages on the installed system or in a transaction. π
- File triggers β invoked conditionally when their specified path prefix matches other files on the installed system or in a transaction. π¦
By default, scriptlets run with the /bin/sh shell, but that can be overridden (peep OPTIONS below). Usually a scriptlet has a body for the interpreter to execute, but the interpreter can also be a standalone, special-purpose program β in that case, the scriptlet body may be omitted. The scriptlet interpreter dependency gets added automatically by RPM, but any other dependencies on tools your scriptlets use? You gotta manually add those in the spec. no cap. π€
Scriptlets cannot be interactive, and should not rely on their output being seen by users. periodt.
π ARGUMENTS π
NAMEβ The name of the subpackage the scriptlet is associated with, within the spec file. If you leave it out, the main package is assumed. Seerpm-spec(5)for the deets. πTRIGGERCONDβ A trigger condition is a name of a package, optionally followed by a version range:NAME [op EVR]whereopis one of<,<=,=,>=or>, andEVRis anrpm-version(7)label. For example:glibc < 2.40. πPATHPREFIXβ A path prefix on which the file trigger is triggered on. The prefix must be a complete path of its own, typically a directory whose contents the trigger is interested in, e.g./usr/lib. π§
π OPTIONS π
-eβ Perform runtimerpm-macros(7)expansion on the scriptlet before executing it. Note that macros intended to be runtime expanded generally need to be escaped in specs to avoid buildtime expansion. π-nβ InterpretNAMEas a full package name. π-p EXECUTABLEβ Execute the scriptlet usingEXECUTABLEas the interpreter. As a special case, specifying<lua>as theEXECUTABLEinvokes the embedded Lua interpreter. The various special behaviors of such scriptlets are documented in therpm-lua(7)manual. π₯-qβ Performrpm-queryformat(7)on the package header, using the scriptlet as the format. Note the need to escape things that look like macros, and constructs that are special to the shell, such as{}and[]. π
π FILETRIGGER OPTIONS π
-P NUMBERβ Priority of the trigger, among the other file triggers of the same kind. The default priority of file triggers is100000. A larger priority means earlier execution. β
π₯ SCRIPTS π₯
%SCRIPT [options] [NAME]
[body]
Scripts execute unconditionally at their pre-determined slots in a transaction. no questions asked. π
Scripts receive one argument ($1), which contains the number of installed instances of the package when the operation on the package containing the executing script completes. RPM performs upgrades as two separate but related operations: install of the new version, and erase of the old version. This can be used to differentiate install, erase and upgrade/downgrade from each other: the argument is 1 on initial install, and 0 on final removal. Otherwise it's an upgrade/downgrade (or parallel install). π
The scripts of a package can be examined with a rpm -q --scripts query. π
π Package scripts π
Package scripts execute before and after the main operation (install/remove etc.) of the package inside a transaction.
Package scripts should only be used for actions that are fundamentally package specific. Domain specific registries, databases, caches and such are much better handled centrally by file triggers. Similarly, user and group creation should be handled by rpm-sysusers(7), rather than scripting them. π
%preβ Executed just before unpacking the contents of the package. Non-zero exit prevents the installation of the containing package. π«Ά%postβ Executed just after unpacking the contents of the package. β¨%preunβ Executed just before removing the contents of the package. Non-zero exit prevents the uninstallation of the containing package. π€%postunβ Executed just after removing the contents of the package. π%verifyβ Executed when a package is verified usingrpm --verify. Unlike all the other scriptlet types,%verifynever executes as a part of install/erase etc. operations. π¦
π Transaction scripts π
Transaction scripts run before and after all the other package level operations (install/remove etc.) in a transaction.
When multiple transaction scripts for a given slot are present in a transaction, they are executed in the order of their install/removal order within the transaction.
%pretransβ Executed just before an install/update/reinstall transaction on the containing package starts. No files from the transaction have been installed or removed yet. That is, in a fresh installation to an empty system root, there are no files around and no interpreter to run, so the only interpreter that can be reliably used in this slot is the embeddedrpm-lua(7)interpreter. Non-zero exit prevents the installation of the containing package. This is a very special and a dangerous slot, and is best avoided. β οΈ be careful bestie β οΈ%posttransβ Executed just after an install/update/reinstall transaction on the containing package finishes. All files from the transaction have been installed or removed at this point. β%preuntransβ Executed just before an uninstall/updated-from transaction on the containing package starts. π%postuntransβ Executed just after an uninstall/updated-from transaction on the containing package finishes. πΈ
π TRIGGERS π
%TRIGGER [options] [NAME] -- TRIGGERCOND ...
[body]
Triggers are package scriptlets that are set off by changes to other packages. Unlike scripts, triggers are only executed when their trigger condition is satisfied. Besides just reacting to changes to other packages, they are often used for version specific migration tasks. π
A trigger scriptlet may have more than one condition, and any of them matching sets off the trigger. A trigger will only run once even if there are multiple packages setting it off. periodt. π
Triggers receive one argument ($1), which contains the number of installed instances of the triggered package (ie. the package containing the trigger scriptlet) when the operation has completed. In addition, triggers receive a second argument ($2), which contains the number of installed instances of the triggering package (ie. the package that set off the trigger). π«Ά
The triggers of a package can be examined with a rpm -q --triggers query. π₯
%triggerpreinβ Executed just before the installation of a triggering package. β%triggerinβ Executed just after the installation of a triggering package. β¨%triggerunβ Executed just before the removal of a triggering package. π%triggerpostunβ Executed just after the removal of a triggering package. π
π§ FILE TRIGGERS π§
%FILETRIGGER [options] [filetrigger-options] [NAME] -- PATHPREFIX ...
[body]
File triggers are scriptlets that are set off by changes to files, ie. triggers whose trigger condition is path-based. File triggers are used to centralize script patterns that repeat across multiple packages, such as registering domain-specific files into databases or updating system-wide caches. π
File triggers receive the matching paths via standard input, one per line. π₯
Like triggers, they receive one argument ($1), which contains the number of installed instances of the triggered package (ie. the package containing the file trigger scriptlet) when the operation has completed.
In addition, Package File Triggers receive a second argument ($2), which contains the number of installed instances of the triggering package (ie. the package which set off the trigger). π
The file triggers of a package can be examined with a rpm -q --filetriggers query. π
π§ Package File Triggers π§
Package file triggers execute once per each triggering package.
%filetriggerinβ Executed just after the installation of each triggering package. πΈ%filetriggerunβ Executed just before the removal of each triggering package. π%filetriggerpostunβ Executed just after the removal of each triggering package. Note: not executed when the package containing the trigger is removed. β οΈ
π Transaction File Triggers π
Transaction file triggers only execute once per transaction, regardless of how many packages triggered it. one and done, bestie. π
%transfiletriggerinβ Executed once at the end of a transaction, for all matching installed files (ie. from the transaction or previously installed). β¨%transfiletriggerunβ Executed once at the start of a transaction, for all matching removed files. π%transfiletriggerpostunβ Executed once at the end of a transaction if there were matching removed files. Note: the list of triggering files is not available in this slot. π
π₯ EXECUTION ORDER π₯
omg bestie sit DOWN because this is the β¨canonicalβ¨ order that scriptlets fire off inside a single transaction, for one package upgrade. Here's the vocab real quick so you're not lost:
newβ the shiny freshly-installed package version πoldβ the version already on your system thatnewis replacing, rip πͺ¦rpmdbβ installed packages, the database bestieanyβ bothrpmdband the other packages in the current transaction
And now the sacred sequence, no cap:
%pretransof new%preuntransof old%transfiletriggerunof any, set off by removal of old%sysusersof new (implicit)%triggerpreinof rpmdb, set off by install of new%triggerpreinof new, set off by rpmdb%preof new- (unpack new files)
%filetriggerinof any (high priority), set off by new%filetriggerinof new (high priority), set off by any%postof new%triggerinof rpmdb, set off by new%triggerinof new, set off by rpmdb%filetriggerinof any (low priority), set off by new%filetriggerinof new (low priority), set off by any%triggerunof old, set off by rpmdb%triggerunof rpmdb, set off by old%filetriggerunof old (high priority), set off by any%filetriggerunof any (high priority), set off by old%preunof old%filetriggerunof old (low priority), set off by any%filetriggerunof any (low priority), set off by old- (erase old files)
%filetriggerpostunof new (high priority), set off by any%filetriggerpostunof any (high priority), set off by new%postunof old%filetriggerpostunof new (low priority), set off by any%filetriggerpostunof any (low priority), set off by new%posttransof new%postuntransof old%transfiletriggerpostunof any, set off by old%transfiletriggerinof any, set off by new
π ENVIRONMENT π
RPM_INSTALL_PREFIX_Nβ the N'th install prefix in relocatable packages. yes bestie, theNis literally the index number, it's giving numbered-slot energy π°RPM_INSTALL_PREFIXβ same asRPM_INSTALL_PREFIX0, but kept around for legacy compatibility. it's the OG vibe, still valid, still iconic π«Ά
π EXIT STATUS π
okay so this part matters, listen up. a non-zero exit code from any scriptlet gets reflected as an error in the transaction result code. and on top of that? a non-zero exit from the various pre-scriptlets (the ones noted earlier for each such scriptlet) stops further processing related to that package β not the whole transaction. so it's a mini-cancel, not a full meltdown π«
π¨ and now the big one: scriptlets should ALWAYS be designed to return with zero exit code, because RPM cannot undo or roll back a transaction. there is no ctrl+z, bestie. none. periodt.
for example: an exit code of 1 from a %pre script prevents the install/update of that package, but the rest of the transaction will still take place, even if there are missing dependencies. yeah. it really said "not my problem" π
β¨ EXAMPLES β¨
Example 1. Update /etc/shell on package install/removal
%post
if [ $1 -eq 1 ]; then
# Initial install
grep -q '^/bin/mysh$' /etc/shells || echo '/bin/mysh' >> /etc/shells
fi
%postun
if [ $1 -eq 0 ] ; then
# Package removal, not upgrade
sed -i '/^/bin/mysh$/d' /etc/shells
fi
it's giving "add me on first install, take me out on removal" and honestly? respectful. tasteful. π
Example 2. Manage alternatives in a subpackage
Name: frobber
[...]
%package myimpl
[...]
%post myimpl
update-alternatives --install /usr/bin/frobber \
frobber /usr/bin/frobber.myimpl 10
%preun myimpl
if [ $1 -eq 0]; then
# Package removal, not upgrade
update-alternatives --remove frobber /usr/bin/frobber.myimpl 10
fi
Example 3. Manage the MIME database
%transfiletriggerin -- /usr/share/mime
/usr/bin/update-mime-database -n /usr/share/mime &> /dev/null ||:
%transfiletriggerpostun -- /usr/share/mime
/usr/bin/update-mime-database -n /usr/share/mime &> /dev/null ||:
note that these would go to the package that owns the /usr/bin/update-mime-database binary π which then centrally handles all updates to the /usr/share/mime directory. and the way that this is smart is that besides removing the need to have such scriptlets in all packages with such content, it also eliminates the need to have dependencies to the helper binary in all related packages. it's giving delegation, it's giving main character of the transaction, obsessed π
Example 4. Enable a systemd preset on upgrade from older version
Name: my
Version: 3.0
[...]
# On update from 2.x
%triggerun -- my < 3.0-1
if [ -x /usr/bin/systemctl ]; then
systemctl --no-reload preset mydb-migrate ||:
fi
Example 5. Execute a program in %pretrans
%pretrans -p <lua>
if posix.access('/usr/bin/mything', 'x') then
rpm.execute('/usr/bin/mything')
end
note that this seemingly simple thing is generally not possible in %pretrans without using -p <lua>, because on the initial install of a system, even /bin/sh is not present at all π like it's genuinely bare-bones in there bestie. nothing to lean on. hence the lua. π§
Example 6. Runtime macro expansion
%post -e
ln -s %{_datadir}/bar/some.ext %%{_libdir}/foo/some.ext
Example 7. Runtime queryformat expansion for installed filenames
%post -q
for f in [%%{instfilenames} ]; do
echo $f
done
the filenames printed by this accurately reflect any file policies from the time of installation, such as --nodocs. so it remembers the choices, fr fr πΈ
π BUGS π
Priority for Transaction File Triggers is not currently implemented. that's it. that's the bug. we're being honest about it π
π SEE ALSO π
rpm(8), rpm-lua(7), rpm-sysusers(7), rpm-spec(5)
okay that's the whole page bestie, you made it, go write some scriptlets π this masterpiece dropped as part of RPM 6.1.0 on 2026-08-20, and the page itself goes by RPM-SCRIPTLETS(7) β slay, stan, and always return zero β¨ππ
π¦ where this page comes from
- ubuntu stonking: rpm 6.1.0-1 Β· GPL-2.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms