A reference for lifecycle hooks across different package manager ecosystems.
5
6 commits
updated Jun 28, 2026
A reference for lifecycle hooks across different package manager ecosystems.
This is part of a larger effort to document package manager data for the ecosyste.ms project.
Package manager hooks fall into two distinct categories:
Package-Defined Hooks: Scripts or code that package authors define to run during their own package's lifecycle (install, build, publish, uninstall). These execute when someone installs or uses your package.
System/Plugin Hooks: Extension points that let users, tools, or plugins modify the package manager's behavior globally across all operations, not tied to any specific package.
This distinction matters for security: package-defined hooks represent supply chain attack surface (third-party code runs on your machine), while system hooks are user-controlled.
| Hook | When it runs |
|---|---|
preinstall | Before package installation |
install | During installation |
postinstall | After installation |
preuninstall | Before uninstallation |
uninstall | During uninstallation |
postuninstall | After uninstallation |
prepublish | Before publish and local install (deprecated behavior) |
prepublishOnly | Before publish only |
prepack | Before tarball is packed |
postpack | After tarball is packed |
prepare | After install (both local and from registry) and before publish |
dependencies | After any dependency changes |
Mechanism: scripts field in package.json
{
"scripts": {
"preinstall": "node check-env.js",
"postinstall": "node build-native.js"
}
}
Documentation:
pnpm-specific hooks:
pnpm:devPreinstall - Runs only on local pnpm install, before any dependency is installed (root package.json only)Security notes:
postinstall is the most common vector for supply chain attacks--ignore-scripts=false)onlyBuiltDependencies in package.json or pnpm approve-builds command (prompted by the Rspack supply chain attack)ignoreScripts, ignoreDepScripts, onlyBuiltDependencies, neverBuiltDependencies, dangerouslyAllowAllBuilds--ignore-scriptsnpm audit, socket.dev, or similar tools to detect suspicious install scriptsDeprecation notes:
prepublish behavior is confusing (runs on install too); use prepublishOnly or prepare insteadprepublish on install in npm 5+| Hook | When it runs |
|---|---|
build.rs | Before compilation, generates code or sets build flags |
Mechanism: Build script at crate root
// build.rs
fn main() {
// Generate code, link native libraries, set cfg flags
println!("cargo:rustc-link-lib=foo");
}
Documentation: https://doc.rust-lang.org/cargo/reference/build-scripts.html
Capabilities:
OUT_DIR)cargo:rustc-* directivescargo:rerun-if-changedSecurity notes:
cargo-vet and cargo-crev can audit dependenciescargo install from crates.io (only during source builds)Additional hooks (via Cargo configuration, not per-package):
Wheel packages (PEP 427): Hooks are explicitly forbidden. Wheels are designed to be "unpack and go" with no arbitrary code execution at install time.
Source distributions (sdist): Can run setup.py which executes arbitrary Python code.
| Hook | When it runs |
|---|---|
setup.py | During pip install from source |
Mechanism: setup.py at package root (legacy) or build backend
# setup.py
from setuptools import setup
from setuptools.command.install import install
class PostInstall(install):
def run(self):
install.run(self)
# custom code here
setup(cmdclass={'install': PostInstall})
Documentation:
Security notes:
pip install --only-binary :all: forces wheel-only installationpyproject.toml) are sandboxed better than raw setup.py--no-build-isolation which can expose the build environmentPoetry/PDM/Hatch: These tools primarily use PEP 517 build backends and don't add their own hook mechanisms for package authors.
| Hook | When it runs |
|---|---|
pre-install-cmd | Before install command |
post-install-cmd | After install command |
pre-update-cmd | Before update command |
post-update-cmd | After update command |
pre-autoload-dump | Before autoloader is generated |
post-autoload-dump | After autoloader is generated |
post-root-package-install | After root package is installed |
post-create-project-cmd | After create-project command |
pre-archive-cmd | Before archive command |
post-archive-cmd | After archive command |
pre-package-install | Before a package is installed |
post-package-install | After a package is installed |
pre-package-update | Before a package is updated |
post-package-update | After a package is updated |
pre-package-uninstall | Before a package is uninstalled |
post-package-uninstall | After a package is uninstalled |
Mechanism: scripts field in composer.json
{
"scripts": {
"post-install-cmd": "MyVendor\\MyClass::postInstall",
"post-package-install": ["php scripts/install.php"]
}
}
Documentation: https://getcomposer.org/doc/articles/scripts.md
Critical security note: Only the root package's scripts are executed. Scripts defined in dependencies are NOT run. This is a deliberate security design decision.
Additional notes:
@composer and @php are available as portable binary references| Hook | When it runs |
|---|---|
extconf.rb | During native extension compilation |
Rakefile tasks | Various build/test tasks |
Mechanism: Extensions in gemspec
# example.gemspec
Gem::Specification.new do |s|
s.extensions = ['ext/example/extconf.rb']
end
Documentation:
Security notes:
extconf.rb runs arbitrary Ruby code during gem installbundle config set --local force_ruby_platform true can help avoid precompiled binariesBundler: Does not add additional hook mechanisms for package authors; uses RubyGems' extension system.
| Hook | When it runs | Status |
|---|---|---|
install.ps1 | After package install | Deprecated in NuGet 3.x |
uninstall.ps1 | Before package uninstall | Deprecated in NuGet 3.x |
init.ps1 | First time package is loaded in solution | Still works in VS |
Mechanism (legacy): PowerShell scripts in package tools/ folder
Documentation: https://learn.microsoft.com/en-us/nuget/concepts/package-installation-process
Current approach: NuGet moved away from install scripts in favor of:
<!-- In .nuspec or package -->
<files>
<file src="build\MyPackage.props" target="build" />
<file src="build\MyPackage.targets" target="build" />
</files>
Security notes:
dotnet restoreMaven uses a build lifecycle with phases, not traditional hooks.
| Phase | Description |
|---|---|
validate | Validate project |
compile | Compile source |
test | Run tests |
package | Package into JAR/WAR |
verify | Run integration tests |
install | Install to local repo |
deploy | Deploy to remote repo |
Mechanism: Plugin executions bound to phases in pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<!-- arbitrary Ant tasks -->
</target>
</configuration>
</execution>
</executions>
</plugin>
Documentation: https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
Security notes:
| Hook | When it runs |
|---|---|
beforeEvaluate | Before project is evaluated |
afterEvaluate | After project is evaluated |
| Task lifecycle hooks | Before/after specific tasks |
Mechanism: Build scripts (build.gradle or build.gradle.kts)
// build.gradle
afterEvaluate {
println "Project evaluated"
}
tasks.named('jar') {
doFirst { println "Before jar" }
doLast { println "After jar" }
}
Documentation: https://docs.gradle.org/current/userguide/build_lifecycle.html
Security notes:
--no-daemon can help with reproducibilitygradlew) should be committed to ensure version consistency| Hook | When it runs |
|---|---|
preinst | Before package files are unpacked |
postinst | After package files are unpacked |
prerm | Before package is removed |
postrm | After package is removed |
config | For debconf configuration (before preinst) |
Mechanism: Shell scripts in DEBIAN/ directory of .deb package
#!/bin/bash
# DEBIAN/postinst
case "$1" in
configure)
systemctl enable myservice
;;
esac
Arguments passed to scripts:
preinst: install, upgrade, abort-upgradepostinst: configure, abort-upgrade, abort-remove, abort-deconfigureprerm: remove, upgrade, deconfigurepostrm: remove, purge, upgrade, disappear, abort-install, abort-upgradeDocumentation: https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html
Security notes:
| Hook | When it runs |
|---|---|
%pretrans | Before any packages in transaction |
%pre | Before package files are installed |
%post | After package files are installed |
%preun | Before package files are removed |
%postun | After package files are removed |
%posttrans | After all packages in transaction |
%triggerprein | Trigger before other package installed |
%triggerin | Trigger when other package installed |
%triggerun | Trigger before other package removed |
%triggerpostun | Trigger after other package removed |
Mechanism: Scriptlets in .spec file
%post
systemctl daemon-reload
systemctl enable myservice
%preun
if [ $1 -eq 0 ]; then
systemctl stop myservice
fi
Documentation: https://rpm-software-management.github.io/rpm/manual/scriptlet_expansion.html
Security notes:
$1 argument indicates install (1), upgrade (2), or uninstall (0)| Hook | When it runs |
|---|---|
prepare_command | After pod is downloaded, before integration |
Mechanism: In podspec
Pod::Spec.new do |s|
s.prepare_command = <<-CMD
sed -i '' 's/old/new/g' SomeFile.m
CMD
end
Documentation: https://guides.cocoapods.org/syntax/podspec.html#prepare_command
Security notes:
prepare_command runs shell commands--no-prepare-command during installSwift Package Manager intentionally does not support lifecycle hooks for security reasons.
Documentation: https://www.swift.org/documentation/package-manager/
Build-time customization:
// Package.swift
.plugin(
name: "MyPlugin",
capability: .buildTool()
)
Plugin documentation: https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md
Go modules do not support lifecycle hooks.
go generate is a separate command that runs directives in source files:
//go:generate stringer -type=Pill
This is opt-in and explicit, not automatic during install.
Documentation:
Security notes:
go get or go installgo generate must be explicitly run| Hook | When it runs |
|---|---|
Setup.hs | Custom build phases |
Mechanism: Custom Setup.hs with build type Custom
-- Setup.hs
import Distribution.Simple
main = defaultMainWithHooks simpleUserHooks
{ postBuild = \args flags desc lbi -> do
putStrLn "Post-build hook"
}
Documentation:
Security notes:
Setup.hs runs at build timebuild-type: Simple to avoid custom setup| Hook | When it runs |
|---|---|
| Compiler callbacks | During compilation |
Mechanism: Mix tasks and compiler hooks
# mix.exs
defmodule MyApp.MixProject do
use Mix.Project
def project do
[
compilers: [:custom_compiler] ++ Mix.compilers()
]
end
end
Documentation: https://hexdocs.pm/mix/Mix.html
Security notes:
| Hook | When it runs |
|---|---|
| Pre/post hooks | Around compile, clean, etc. |
Mechanism: rebar.config
{pre_hooks, [{compile, "echo pre-compile"}]}.
{post_hooks, [{compile, "echo post-compile"}]}.
Documentation: https://rebar3.readme.io/docs/configuration#hooks
While pub (as a package manager) does not support lifecycle hooks for packages, most
Dart embedders (Flutter, dart run and dart build) do.
| Hook | When it runs |
|---|---|
| Build hooks | During compilation |
Documentation: https://dart.dev/tools/hooks
Security notes:
hook/build.dart runs arbitrary Dart code.| Hook | When it runs |
|---|---|
Makefile.PL | Before build |
Build.PL | Before build (Module::Build) |
Mechanism: Perl scripts that generate makefiles
# Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'My::Module',
# custom code can run here
);
Documentation: https://metacpan.org/pod/ExtUtils::MakeMaker
Security notes:
Makefile.PL runs arbitrary Perl codecpanm --notest to skip test phase but not install scripts| Hook | When it runs |
|---|---|
build.install.bin | Install executables |
| Build type hooks | During build phase |
Mechanism: Rockspec file
-- myrock-1.0-1.rockspec
build = {
type = "command",
build_command = "make",
install_command = "make install"
}
Documentation: https://github.com/luarocks/luarocks/wiki/Rockspec-format
| Hook | When it runs |
|---|---|
build() | During package build |
package() | During package creation |
Mechanism: conanfile.py
class MyConan(ConanFile):
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
Documentation: https://docs.conan.io/2/reference/conanfile.html
| Hook | When it runs |
|---|---|
build | Build commands |
install | Install commands |
remove | Uninstall commands |
Mechanism: opam file
build: ["dune" "build" "-p" name "-j" jobs]
install: ["dune" "install" "-p" name]
Documentation: https://opam.ocaml.org/doc/Manual.html
| Hook | When it runs |
|---|---|
| Various hooks | Around any Leiningen task |
Mechanism: project.clj or plugins
;; project.clj
:hooks [leiningen.hooks.my-hook]
Documentation: https://codeberg.org/leiningen/leiningen/src/branch/main/doc/PLUGINS.md
| Hook | When it runs |
|---|---|
pre-install | Before package install |
post-install | After package install |
pre-deinstall | Before package removal |
post-deinstall | After package removal |
pre-upgrade | Before package upgrade |
post-upgrade | After package upgrade |
Mechanism: Scripts in .INSTALL file within package
Documentation: https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package
| Hook | When it runs |
|---|---|
pre_install | Before install |
post_install | After install |
pre_upgrade | Before upgrade |
post_upgrade | After upgrade |
pre_remove | Before removal |
post_remove | After removal |
Mechanism: Install scriptlet in PKGBUILD
post_install() {
systemctl daemon-reload
}
Documentation: https://wiki.archlinux.org/title/PKGBUILD#install
| Hook | When it runs |
|---|---|
pre-install | Before installation |
post-install | After installation |
pre-deinstall | Before removal |
post-deinstall | After removal |
Mechanism: Shell scripts in package
Documentation: https://docs.freebsd.org/en/books/porters-handbook/pkg-files/
| Hook | When it runs |
|---|---|
post_install | After installation |
Mechanism: Ruby block in formula
class MyFormula < Formula
def post_install
system "some-command"
end
end
Documentation: https://docs.brew.sh/Formula-Cookbook
Security notes:
Nix derivations are pure by design—no hooks that run arbitrary code outside the sandbox.
Build phases exist but run in isolated environments:
{
postInstall = ''
wrapProgram $out/bin/foo
'';
}
Documentation: https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases
Security notes:
Docker images don't have "install hooks" but have build-time instructions:
| Instruction | When it runs |
|---|---|
RUN | During image build |
ENTRYPOINT | When container starts |
CMD | When container starts (if no command given) |
HEALTHCHECK | Periodically after container starts |
Mechanism: Dockerfile / Containerfile
Documentation:
Terraform providers don't have lifecycle hooks in the traditional sense. Resources have:
| Lifecycle | Purpose |
|---|---|
create_before_destroy | Order of operations |
prevent_destroy | Safety mechanism |
ignore_changes | Drift handling |
| Provisioners | Run scripts (discouraged) |
Mechanism: lifecycle block and provisioners
resource "aws_instance" "example" {
lifecycle {
create_before_destroy = true
}
provisioner "local-exec" {
command = "echo created"
}
}
Documentation: https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax
Security notes:
| Hook | When it runs |
|---|---|
pre-install | Before resources are loaded |
post-install | After all resources are loaded |
pre-delete | Before resources are deleted |
post-delete | After resources are deleted |
pre-upgrade | Before resources are updated |
post-upgrade | After resources are updated |
pre-rollback | Before rollback |
post-rollback | After rollback |
test | When helm test is run |
Mechanism: Annotations on Kubernetes resources
apiVersion: batch/v1
kind: Job
metadata:
annotations:
"helm.sh/hook": post-install
Documentation: https://helm.sh/docs/topics/charts_hooks/
Ansible roles/collections don't have install-time hooks. Execution happens when playbooks run.
Documentation: https://docs.ansible.com/ansible/latest/galaxy/user_guide.html
| Hook | When it runs |
|---|---|
configure | On install, refresh, or snap set |
install | After snap install |
pre-refresh | Before snap refresh |
post-refresh | After snap refresh |
remove | Before snap removal |
Mechanism: Scripts in snap/hooks/
Documentation: https://snapcraft.io/docs/supported-snap-hooks
Security notes:
| Hook | When it runs |
|---|---|
pre_install | Before package install |
post_install | After package install |
pre_remove | Before package removal |
post_remove | After package removal |
Mechanism: Functions in package template
Documentation: https://github.com/void-linux/void-packages/blob/master/Manual.md
| Hook | When it runs |
|---|---|
pkg_pretend | Check system requirements |
pkg_setup | Before compilation |
pkg_preinst | Before merge |
pkg_postinst | After merge |
pkg_prerm | Before unmerge |
pkg_postrm | After unmerge |
pkg_config | User-triggered configuration |
Mechanism: Functions in ebuild file
pkg_postinst() {
elog "Run 'emerge --config ${PN}' to configure"
}
Documentation: https://devmanual.gentoo.org/ebuild-writing/functions/
| Hook | When it runs |
|---|---|
doinst.sh | After package extraction |
Mechanism: Script in package install/ directory
Documentation: https://www.slackbook.org/html/package-management.html
Zypper uses RPM packages, so RPM scriptlets apply. See RPM section above.
Additional zypper hooks: Zypper itself supports commit plugins and service hooks.
Documentation: https://doc.opensuse.org/documentation/leap/reference/html/book-reference/cha-sw-cl.html
Uses RPM packages. See RPM section above.
Documentation: https://wiki.mageia.org/en/URPMI
Uses RPM packages. See RPM section above.
Documentation: https://www.pclinuxos.com/forum/
| Hook | When it runs |
|---|---|
pre-link | Before package is linked |
post-link | After package is linked |
pre-unlink | Before package is unlinked |
Mechanism: Scripts in <package>/Scripts/ (Windows) or <package>/bin/ (Unix)
#!/bin/bash
# bin/.mypackage-post-link.sh
echo "Package installed"
Documentation:
Security notes:
Most system package managers (zypper, dnf, yum, urpmi, etc.) defer to the underlying package format (RPM, deb) for scriptlets. They add repository-level and transaction-level hooks (see System/Plugin Hooks section).
| Package Manager | Hooks Supported | Security Model |
|---|---|---|
| npm/Yarn/pnpm/Bun | Yes | Scripts run, can be disabled |
| Cargo | build.rs only | Compile-time only |
| pip (wheel) | No | By design |
| pip (sdist) | Yes | setup.py runs |
| Composer | Root package only | Dependencies' scripts ignored |
| RubyGems | Extensions only | extconf.rb for native code |
| NuGet | Deprecated | MSBuild targets only |
| Maven | Build plugins | No install-time hooks |
| Gradle | Build scripts | No install-time hooks |
| dpkg/RPM | Yes | Run as root |
| CocoaPods | prepare_command | Shell command |
| SwiftPM | No | By design |
| Go | No | By design |
| Nix | Sandboxed | Pure builds |
| Conda | Yes | Scripts in package |
These hooks let users extend package manager behavior globally, independent of specific packages.
| Hook Type | Description |
|---|---|
.npmrc scripts | Not supported |
| npm hooks (deprecated) | Webhooks for registry events |
npm does not have a local plugin/hook system for extending CLI behavior. The npm hook command was for registry webhooks (deprecated).
Workaround: Use shell aliases or wrapper scripts.
Documentation: https://docs.npmjs.com/cli/v10/commands/npm-hook
Yarn Classic (1.x): No plugin system.
Yarn Berry (2+): Full plugin system.
| Hook | Description |
|---|---|
afterAllInstalled | After all packages installed |
validateProject | Validate project configuration |
resolveStep | Custom resolution logic |
fetchStep | Custom fetch logic |
linkStep | Custom linking logic |
Mechanism: Yarn plugins (JavaScript)
module.exports = {
name: 'my-plugin',
factory: require => ({
hooks: {
afterAllInstalled() {
console.log('Done!');
},
},
}),
};
Documentation: https://yarnpkg.com/advanced/plugin-tutorial
| Hook | Description | Version |
|---|---|---|
readPackage | Mutate dependency's package.json after parsing | - |
afterAllResolved | Mutate lockfile after dependencies resolved | - |
preResolution | Modify lockfile before resolution | - |
importPackage | Customize how packages are written to node_modules | - |
fetchers | Override fetchers for different dependency types | - |
updateConfig | Modify pnpm configuration | v10.8.0 |
Mechanism: .pnpmfile.cjs hook file at project root
// .pnpmfile.cjs
module.exports = {
hooks: {
readPackage(pkg) {
// Modify pkg before resolution
return pkg;
},
afterAllResolved(lockfile) {
// Mutate lockfile before serialization
return lockfile;
},
},
};
Documentation: https://pnpm.io/pnpmfile
| Hook | Description |
|---|---|
[alias] | Command aliases |
| Custom subcommands | cargo-* binaries in PATH |
Mechanism: .cargo/config.toml and PATH
# .cargo/config.toml
[alias]
b = "build"
mycommand = ["run", "--bin", "mytool"]
Custom subcommands: Any cargo-foo binary in PATH becomes cargo foo.
Documentation: https://doc.rust-lang.org/cargo/reference/config.html
No plugin or hook system for extending pip/uv behavior.
Documentation:
| Hook | Description |
|---|---|
| Plugins | Extend Poetry functionality |
Mechanism: Poetry plugins (Python packages)
from poetry.plugins.plugin import Plugin
class MyPlugin(Plugin):
def activate(self, poetry, io):
pass
Documentation: https://python-poetry.org/docs/plugins/
| Hook | Description |
|---|---|
| Bundler plugins | Extend Bundler functionality |
Mechanism: Plugins via gems
# Register hooks
Bundler::Plugin.add_hook('after-install') do |deps|
puts "Installed: #{deps.map(&:name)}"
end
Documentation: https://bundler.io/guides/bundler_plugins.html
| Hook | Description |
|---|---|
| Composer plugins | Extend Composer |
Mechanism: Packages of type composer-plugin
class MyPlugin implements PluginInterface, EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
'post-install-cmd' => 'onPostInstall',
];
}
}
Documentation: https://getcomposer.org/doc/articles/plugins.md
| Hook | Description |
|---|---|
| Extensions | Modify Maven core |
settings.xml profiles | Global configuration |
Mechanism: .mvn/extensions.xml
<extensions>
<extension>
<groupId>com.example</groupId>
<artifactId>my-extension</artifactId>
<version>1.0</version>
</extension>
</extensions>
Documentation: https://maven.apache.org/guides/mini/guide-using-extensions.html
| Hook | Description |
|---|---|
| Init scripts | Run before every build |
| Settings plugins | Modify settings |
| Convention plugins | Reusable build logic |
Mechanism: ~/.gradle/init.gradle or ~/.gradle/init.d/*.gradle
// ~/.gradle/init.gradle
allprojects {
repositories {
mavenLocal()
}
}
Documentation: https://docs.gradle.org/current/userguide/init_scripts.html
| Hook | Description |
|---|---|
APT::Update::Pre-Invoke | Before update |
APT::Update::Post-Invoke | After update |
DPkg::Pre-Invoke | Before dpkg runs |
DPkg::Post-Invoke | After dpkg runs |
Mechanism: /etc/apt/apt.conf.d/
// /etc/apt/apt.conf.d/99custom
DPkg::Post-Invoke { "echo 'dpkg finished'"; };
Documentation: https://manpages.debian.org/stable/apt/apt.conf.5.en.html
| Hook | Description |
|---|---|
| Plugins | Extend dnf functionality |
| Transaction hooks | Before/after transactions |
Mechanism: Python plugins in /etc/dnf/plugins/ or /usr/lib/python*/site-packages/dnf-plugins/
Documentation: https://dnf.readthedocs.io/en/latest/api_plugins.html
| Hook | Description |
|---|---|
| Alpm hooks | Run scripts on file triggers |
Mechanism: /etc/pacman.d/hooks/*.hook
[Trigger]
Type = File
Operation = Install
Target = usr/lib/modules/*/vmlinuz
[Action]
When = PostTransaction
Exec = /usr/bin/mkinitcpio -P
Documentation: https://man.archlinux.org/man/alpm-hooks.5
| Hook | Description |
|---|---|
| Taps | Third-party formula repos |
| External commands | brew-* executables in PATH |
Mechanism: Any brew-foo script in PATH becomes brew foo.
Documentation: https://docs.brew.sh/External-Commands
| Hook | Description |
|---|---|
| Overlays | Modify package set |
| Flakes | Composable configurations |
Mechanism: ~/.config/nixpkgs/overlays/
# ~/.config/nixpkgs/overlays/my-overlay.nix
self: super: {
myPackage = super.myPackage.override { ... };
}
Documentation: https://nixos.wiki/wiki/Overlays
| Hook | Description |
|---|---|
| BuildKit frontends | Custom Dockerfile syntax |
| Registry webhooks | External notification |
Documentation:
| Hook | Description |
|---|---|
| Plugins | Extend Helm CLI |
| Post-renderers | Modify manifests before apply |
Mechanism: Plugins installed via helm plugin install
Documentation: https://helm.sh/docs/topics/plugins/
| Hook | Description |
|---|---|
| Provider plugins | Add resource types |
| External data sources | Call external programs |
Mechanism: Provider binaries in plugin cache
Documentation: https://developer.hashicorp.com/terraform/plugin
| Hook | Description |
|---|---|
bashrc hooks | Run before/after phases |
| Portage hooks | Global event hooks |
Mechanism: /etc/portage/bashrc and /etc/portage/env/
# /etc/portage/bashrc
post_src_install() {
einfo "Custom post-install hook"
}
Documentation: https://wiki.gentoo.org/wiki//etc/portage/bashrc
| Package Manager | Plugin System | Config Hooks | External Commands |
|---|---|---|---|
| npm | No | No | No |
| Yarn Berry | Yes | Yes | No |
| pnpm | Yes (.pnpmfile.cjs) | Yes (v10.8.0+) | No |
| Cargo | No | Yes (config) | Yes (cargo-*) |
| pip/uv | No | No | No |
| Poetry | Yes | No | No |
| Bundler | Yes | No | No |
| Composer | Yes | No | No |
| Maven | Yes (extensions) | Yes | No |
| Gradle | Yes | Yes (init scripts) | No |
| apt | No | Yes | No |
| dnf/yum | Yes | Yes | No |
| pacman | No | Yes (alpm hooks) | No |
| Homebrew | No | No | Yes (brew-*) |
| Nix | Yes (overlays) | Yes | No |
| Helm | Yes | No | No |
| Portage | No | Yes (bashrc) | No |
This document is released under CC0 1.0 Universal (Public Domain).
187 followers · starred Sep 2026
A reference for lifecycle hooks across different package manager ecosystems.
5
6 commits
updated Jun 28, 2026
A reference for lifecycle hooks across different package manager ecosystems.
This is part of a larger effort to document package manager data for the ecosyste.ms project.
Package manager hooks fall into two distinct categories:
Package-Defined Hooks: Scripts or code that package authors define to run during their own package's lifecycle (install, build, publish, uninstall). These execute when someone installs or uses your package.
System/Plugin Hooks: Extension points that let users, tools, or plugins modify the package manager's behavior globally across all operations, not tied to any specific package.
This distinction matters for security: package-defined hooks represent supply chain attack surface (third-party code runs on your machine), while system hooks are user-controlled.
| Hook | When it runs |
|---|---|
preinstall | Before package installation |
install | During installation |
postinstall | After installation |
preuninstall | Before uninstallation |
uninstall | During uninstallation |
postuninstall | After uninstallation |
prepublish | Before publish and local install (deprecated behavior) |
prepublishOnly | Before publish only |
prepack | Before tarball is packed |
postpack | After tarball is packed |
prepare | After install (both local and from registry) and before publish |
dependencies | After any dependency changes |
Mechanism: scripts field in package.json
{
"scripts": {
"preinstall": "node check-env.js",
"postinstall": "node build-native.js"
}
}
Documentation:
pnpm-specific hooks:
pnpm:devPreinstall - Runs only on local pnpm install, before any dependency is installed (root package.json only)Security notes:
postinstall is the most common vector for supply chain attacks--ignore-scripts=false)onlyBuiltDependencies in package.json or pnpm approve-builds command (prompted by the Rspack supply chain attack)ignoreScripts, ignoreDepScripts, onlyBuiltDependencies, neverBuiltDependencies, dangerouslyAllowAllBuilds--ignore-scriptsnpm audit, socket.dev, or similar tools to detect suspicious install scriptsDeprecation notes:
prepublish behavior is confusing (runs on install too); use prepublishOnly or prepare insteadprepublish on install in npm 5+| Hook | When it runs |
|---|---|
build.rs | Before compilation, generates code or sets build flags |
Mechanism: Build script at crate root
// build.rs
fn main() {
// Generate code, link native libraries, set cfg flags
println!("cargo:rustc-link-lib=foo");
}
Documentation: https://doc.rust-lang.org/cargo/reference/build-scripts.html
Capabilities:
OUT_DIR)cargo:rustc-* directivescargo:rerun-if-changedSecurity notes:
cargo-vet and cargo-crev can audit dependenciescargo install from crates.io (only during source builds)Additional hooks (via Cargo configuration, not per-package):
Wheel packages (PEP 427): Hooks are explicitly forbidden. Wheels are designed to be "unpack and go" with no arbitrary code execution at install time.
Source distributions (sdist): Can run setup.py which executes arbitrary Python code.
| Hook | When it runs |
|---|---|
setup.py | During pip install from source |
Mechanism: setup.py at package root (legacy) or build backend
# setup.py
from setuptools import setup
from setuptools.command.install import install
class PostInstall(install):
def run(self):
install.run(self)
# custom code here
setup(cmdclass={'install': PostInstall})
Documentation:
Security notes:
pip install --only-binary :all: forces wheel-only installationpyproject.toml) are sandboxed better than raw setup.py--no-build-isolation which can expose the build environmentPoetry/PDM/Hatch: These tools primarily use PEP 517 build backends and don't add their own hook mechanisms for package authors.
| Hook | When it runs |
|---|---|
pre-install-cmd | Before install command |
post-install-cmd | After install command |
pre-update-cmd | Before update command |
post-update-cmd | After update command |
pre-autoload-dump | Before autoloader is generated |
post-autoload-dump | After autoloader is generated |
post-root-package-install | After root package is installed |
post-create-project-cmd | After create-project command |
pre-archive-cmd | Before archive command |
post-archive-cmd | After archive command |
pre-package-install | Before a package is installed |
post-package-install | After a package is installed |
pre-package-update | Before a package is updated |
post-package-update | After a package is updated |
pre-package-uninstall | Before a package is uninstalled |
post-package-uninstall | After a package is uninstalled |
Mechanism: scripts field in composer.json
{
"scripts": {
"post-install-cmd": "MyVendor\\MyClass::postInstall",
"post-package-install": ["php scripts/install.php"]
}
}
Documentation: https://getcomposer.org/doc/articles/scripts.md
Critical security note: Only the root package's scripts are executed. Scripts defined in dependencies are NOT run. This is a deliberate security design decision.
Additional notes:
@composer and @php are available as portable binary references| Hook | When it runs |
|---|---|
extconf.rb | During native extension compilation |
Rakefile tasks | Various build/test tasks |
Mechanism: Extensions in gemspec
# example.gemspec
Gem::Specification.new do |s|
s.extensions = ['ext/example/extconf.rb']
end
Documentation:
Security notes:
extconf.rb runs arbitrary Ruby code during gem installbundle config set --local force_ruby_platform true can help avoid precompiled binariesBundler: Does not add additional hook mechanisms for package authors; uses RubyGems' extension system.
| Hook | When it runs | Status |
|---|---|---|
install.ps1 | After package install | Deprecated in NuGet 3.x |
uninstall.ps1 | Before package uninstall | Deprecated in NuGet 3.x |
init.ps1 | First time package is loaded in solution | Still works in VS |
Mechanism (legacy): PowerShell scripts in package tools/ folder
Documentation: https://learn.microsoft.com/en-us/nuget/concepts/package-installation-process
Current approach: NuGet moved away from install scripts in favor of:
<!-- In .nuspec or package -->
<files>
<file src="build\MyPackage.props" target="build" />
<file src="build\MyPackage.targets" target="build" />
</files>
Security notes:
dotnet restoreMaven uses a build lifecycle with phases, not traditional hooks.
| Phase | Description |
|---|---|
validate | Validate project |
compile | Compile source |
test | Run tests |
package | Package into JAR/WAR |
verify | Run integration tests |
install | Install to local repo |
deploy | Deploy to remote repo |
Mechanism: Plugin executions bound to phases in pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals><goal>run</goal></goals>
<configuration>
<target>
<!-- arbitrary Ant tasks -->
</target>
</configuration>
</execution>
</executions>
</plugin>
Documentation: https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
Security notes:
| Hook | When it runs |
|---|---|
beforeEvaluate | Before project is evaluated |
afterEvaluate | After project is evaluated |
| Task lifecycle hooks | Before/after specific tasks |
Mechanism: Build scripts (build.gradle or build.gradle.kts)
// build.gradle
afterEvaluate {
println "Project evaluated"
}
tasks.named('jar') {
doFirst { println "Before jar" }
doLast { println "After jar" }
}
Documentation: https://docs.gradle.org/current/userguide/build_lifecycle.html
Security notes:
--no-daemon can help with reproducibilitygradlew) should be committed to ensure version consistency| Hook | When it runs |
|---|---|
preinst | Before package files are unpacked |
postinst | After package files are unpacked |
prerm | Before package is removed |
postrm | After package is removed |
config | For debconf configuration (before preinst) |
Mechanism: Shell scripts in DEBIAN/ directory of .deb package
#!/bin/bash
# DEBIAN/postinst
case "$1" in
configure)
systemctl enable myservice
;;
esac
Arguments passed to scripts:
preinst: install, upgrade, abort-upgradepostinst: configure, abort-upgrade, abort-remove, abort-deconfigureprerm: remove, upgrade, deconfigurepostrm: remove, purge, upgrade, disappear, abort-install, abort-upgradeDocumentation: https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html
Security notes:
| Hook | When it runs |
|---|---|
%pretrans | Before any packages in transaction |
%pre | Before package files are installed |
%post | After package files are installed |
%preun | Before package files are removed |
%postun | After package files are removed |
%posttrans | After all packages in transaction |
%triggerprein | Trigger before other package installed |
%triggerin | Trigger when other package installed |
%triggerun | Trigger before other package removed |
%triggerpostun | Trigger after other package removed |
Mechanism: Scriptlets in .spec file
%post
systemctl daemon-reload
systemctl enable myservice
%preun
if [ $1 -eq 0 ]; then
systemctl stop myservice
fi
Documentation: https://rpm-software-management.github.io/rpm/manual/scriptlet_expansion.html
Security notes:
$1 argument indicates install (1), upgrade (2), or uninstall (0)| Hook | When it runs |
|---|---|
prepare_command | After pod is downloaded, before integration |
Mechanism: In podspec
Pod::Spec.new do |s|
s.prepare_command = <<-CMD
sed -i '' 's/old/new/g' SomeFile.m
CMD
end
Documentation: https://guides.cocoapods.org/syntax/podspec.html#prepare_command
Security notes:
prepare_command runs shell commands--no-prepare-command during installSwift Package Manager intentionally does not support lifecycle hooks for security reasons.
Documentation: https://www.swift.org/documentation/package-manager/
Build-time customization:
// Package.swift
.plugin(
name: "MyPlugin",
capability: .buildTool()
)
Plugin documentation: https://github.com/apple/swift-evolution/blob/main/proposals/0303-swiftpm-extensible-build-tools.md
Go modules do not support lifecycle hooks.
go generate is a separate command that runs directives in source files:
//go:generate stringer -type=Pill
This is opt-in and explicit, not automatic during install.
Documentation:
Security notes:
go get or go installgo generate must be explicitly run| Hook | When it runs |
|---|---|
Setup.hs | Custom build phases |
Mechanism: Custom Setup.hs with build type Custom
-- Setup.hs
import Distribution.Simple
main = defaultMainWithHooks simpleUserHooks
{ postBuild = \args flags desc lbi -> do
putStrLn "Post-build hook"
}
Documentation:
Security notes:
Setup.hs runs at build timebuild-type: Simple to avoid custom setup| Hook | When it runs |
|---|---|
| Compiler callbacks | During compilation |
Mechanism: Mix tasks and compiler hooks
# mix.exs
defmodule MyApp.MixProject do
use Mix.Project
def project do
[
compilers: [:custom_compiler] ++ Mix.compilers()
]
end
end
Documentation: https://hexdocs.pm/mix/Mix.html
Security notes:
| Hook | When it runs |
|---|---|
| Pre/post hooks | Around compile, clean, etc. |
Mechanism: rebar.config
{pre_hooks, [{compile, "echo pre-compile"}]}.
{post_hooks, [{compile, "echo post-compile"}]}.
Documentation: https://rebar3.readme.io/docs/configuration#hooks
While pub (as a package manager) does not support lifecycle hooks for packages, most
Dart embedders (Flutter, dart run and dart build) do.
| Hook | When it runs |
|---|---|
| Build hooks | During compilation |
Documentation: https://dart.dev/tools/hooks
Security notes:
hook/build.dart runs arbitrary Dart code.| Hook | When it runs |
|---|---|
Makefile.PL | Before build |
Build.PL | Before build (Module::Build) |
Mechanism: Perl scripts that generate makefiles
# Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'My::Module',
# custom code can run here
);
Documentation: https://metacpan.org/pod/ExtUtils::MakeMaker
Security notes:
Makefile.PL runs arbitrary Perl codecpanm --notest to skip test phase but not install scripts| Hook | When it runs |
|---|---|
build.install.bin | Install executables |
| Build type hooks | During build phase |
Mechanism: Rockspec file
-- myrock-1.0-1.rockspec
build = {
type = "command",
build_command = "make",
install_command = "make install"
}
Documentation: https://github.com/luarocks/luarocks/wiki/Rockspec-format
| Hook | When it runs |
|---|---|
build() | During package build |
package() | During package creation |
Mechanism: conanfile.py
class MyConan(ConanFile):
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
Documentation: https://docs.conan.io/2/reference/conanfile.html
| Hook | When it runs |
|---|---|
build | Build commands |
install | Install commands |
remove | Uninstall commands |
Mechanism: opam file
build: ["dune" "build" "-p" name "-j" jobs]
install: ["dune" "install" "-p" name]
Documentation: https://opam.ocaml.org/doc/Manual.html
| Hook | When it runs |
|---|---|
| Various hooks | Around any Leiningen task |
Mechanism: project.clj or plugins
;; project.clj
:hooks [leiningen.hooks.my-hook]
Documentation: https://codeberg.org/leiningen/leiningen/src/branch/main/doc/PLUGINS.md
| Hook | When it runs |
|---|---|
pre-install | Before package install |
post-install | After package install |
pre-deinstall | Before package removal |
post-deinstall | After package removal |
pre-upgrade | Before package upgrade |
post-upgrade | After package upgrade |
Mechanism: Scripts in .INSTALL file within package
Documentation: https://wiki.alpinelinux.org/wiki/Creating_an_Alpine_package
| Hook | When it runs |
|---|---|
pre_install | Before install |
post_install | After install |
pre_upgrade | Before upgrade |
post_upgrade | After upgrade |
pre_remove | Before removal |
post_remove | After removal |
Mechanism: Install scriptlet in PKGBUILD
post_install() {
systemctl daemon-reload
}
Documentation: https://wiki.archlinux.org/title/PKGBUILD#install
| Hook | When it runs |
|---|---|
pre-install | Before installation |
post-install | After installation |
pre-deinstall | Before removal |
post-deinstall | After removal |
Mechanism: Shell scripts in package
Documentation: https://docs.freebsd.org/en/books/porters-handbook/pkg-files/
| Hook | When it runs |
|---|---|
post_install | After installation |
Mechanism: Ruby block in formula
class MyFormula < Formula
def post_install
system "some-command"
end
end
Documentation: https://docs.brew.sh/Formula-Cookbook
Security notes:
Nix derivations are pure by design—no hooks that run arbitrary code outside the sandbox.
Build phases exist but run in isolated environments:
{
postInstall = ''
wrapProgram $out/bin/foo
'';
}
Documentation: https://nixos.org/manual/nixpkgs/stable/#sec-stdenv-phases
Security notes:
Docker images don't have "install hooks" but have build-time instructions:
| Instruction | When it runs |
|---|---|
RUN | During image build |
ENTRYPOINT | When container starts |
CMD | When container starts (if no command given) |
HEALTHCHECK | Periodically after container starts |
Mechanism: Dockerfile / Containerfile
Documentation:
Terraform providers don't have lifecycle hooks in the traditional sense. Resources have:
| Lifecycle | Purpose |
|---|---|
create_before_destroy | Order of operations |
prevent_destroy | Safety mechanism |
ignore_changes | Drift handling |
| Provisioners | Run scripts (discouraged) |
Mechanism: lifecycle block and provisioners
resource "aws_instance" "example" {
lifecycle {
create_before_destroy = true
}
provisioner "local-exec" {
command = "echo created"
}
}
Documentation: https://developer.hashicorp.com/terraform/language/resources/provisioners/syntax
Security notes:
| Hook | When it runs |
|---|---|
pre-install | Before resources are loaded |
post-install | After all resources are loaded |
pre-delete | Before resources are deleted |
post-delete | After resources are deleted |
pre-upgrade | Before resources are updated |
post-upgrade | After resources are updated |
pre-rollback | Before rollback |
post-rollback | After rollback |
test | When helm test is run |
Mechanism: Annotations on Kubernetes resources
apiVersion: batch/v1
kind: Job
metadata:
annotations:
"helm.sh/hook": post-install
Documentation: https://helm.sh/docs/topics/charts_hooks/
Ansible roles/collections don't have install-time hooks. Execution happens when playbooks run.
Documentation: https://docs.ansible.com/ansible/latest/galaxy/user_guide.html
| Hook | When it runs |
|---|---|
configure | On install, refresh, or snap set |
install | After snap install |
pre-refresh | Before snap refresh |
post-refresh | After snap refresh |
remove | Before snap removal |
Mechanism: Scripts in snap/hooks/
Documentation: https://snapcraft.io/docs/supported-snap-hooks
Security notes:
| Hook | When it runs |
|---|---|
pre_install | Before package install |
post_install | After package install |
pre_remove | Before package removal |
post_remove | After package removal |
Mechanism: Functions in package template
Documentation: https://github.com/void-linux/void-packages/blob/master/Manual.md
| Hook | When it runs |
|---|---|
pkg_pretend | Check system requirements |
pkg_setup | Before compilation |
pkg_preinst | Before merge |
pkg_postinst | After merge |
pkg_prerm | Before unmerge |
pkg_postrm | After unmerge |
pkg_config | User-triggered configuration |
Mechanism: Functions in ebuild file
pkg_postinst() {
elog "Run 'emerge --config ${PN}' to configure"
}
Documentation: https://devmanual.gentoo.org/ebuild-writing/functions/
| Hook | When it runs |
|---|---|
doinst.sh | After package extraction |
Mechanism: Script in package install/ directory
Documentation: https://www.slackbook.org/html/package-management.html
Zypper uses RPM packages, so RPM scriptlets apply. See RPM section above.
Additional zypper hooks: Zypper itself supports commit plugins and service hooks.
Documentation: https://doc.opensuse.org/documentation/leap/reference/html/book-reference/cha-sw-cl.html
Uses RPM packages. See RPM section above.
Documentation: https://wiki.mageia.org/en/URPMI
Uses RPM packages. See RPM section above.
Documentation: https://www.pclinuxos.com/forum/
| Hook | When it runs |
|---|---|
pre-link | Before package is linked |
post-link | After package is linked |
pre-unlink | Before package is unlinked |
Mechanism: Scripts in <package>/Scripts/ (Windows) or <package>/bin/ (Unix)
#!/bin/bash
# bin/.mypackage-post-link.sh
echo "Package installed"
Documentation:
Security notes:
Most system package managers (zypper, dnf, yum, urpmi, etc.) defer to the underlying package format (RPM, deb) for scriptlets. They add repository-level and transaction-level hooks (see System/Plugin Hooks section).
| Package Manager | Hooks Supported | Security Model |
|---|---|---|
| npm/Yarn/pnpm/Bun | Yes | Scripts run, can be disabled |
| Cargo | build.rs only | Compile-time only |
| pip (wheel) | No | By design |
| pip (sdist) | Yes | setup.py runs |
| Composer | Root package only | Dependencies' scripts ignored |
| RubyGems | Extensions only | extconf.rb for native code |
| NuGet | Deprecated | MSBuild targets only |
| Maven | Build plugins | No install-time hooks |
| Gradle | Build scripts | No install-time hooks |
| dpkg/RPM | Yes | Run as root |
| CocoaPods | prepare_command | Shell command |
| SwiftPM | No | By design |
| Go | No | By design |
| Nix | Sandboxed | Pure builds |
| Conda | Yes | Scripts in package |
These hooks let users extend package manager behavior globally, independent of specific packages.
| Hook Type | Description |
|---|---|
.npmrc scripts | Not supported |
| npm hooks (deprecated) | Webhooks for registry events |
npm does not have a local plugin/hook system for extending CLI behavior. The npm hook command was for registry webhooks (deprecated).
Workaround: Use shell aliases or wrapper scripts.
Documentation: https://docs.npmjs.com/cli/v10/commands/npm-hook
Yarn Classic (1.x): No plugin system.
Yarn Berry (2+): Full plugin system.
| Hook | Description |
|---|---|
afterAllInstalled | After all packages installed |
validateProject | Validate project configuration |
resolveStep | Custom resolution logic |
fetchStep | Custom fetch logic |
linkStep | Custom linking logic |
Mechanism: Yarn plugins (JavaScript)
module.exports = {
name: 'my-plugin',
factory: require => ({
hooks: {
afterAllInstalled() {
console.log('Done!');
},
},
}),
};
Documentation: https://yarnpkg.com/advanced/plugin-tutorial
| Hook | Description | Version |
|---|---|---|
readPackage | Mutate dependency's package.json after parsing | - |
afterAllResolved | Mutate lockfile after dependencies resolved | - |
preResolution | Modify lockfile before resolution | - |
importPackage | Customize how packages are written to node_modules | - |
fetchers | Override fetchers for different dependency types | - |
updateConfig | Modify pnpm configuration | v10.8.0 |
Mechanism: .pnpmfile.cjs hook file at project root
// .pnpmfile.cjs
module.exports = {
hooks: {
readPackage(pkg) {
// Modify pkg before resolution
return pkg;
},
afterAllResolved(lockfile) {
// Mutate lockfile before serialization
return lockfile;
},
},
};
Documentation: https://pnpm.io/pnpmfile
| Hook | Description |
|---|---|
[alias] | Command aliases |
| Custom subcommands | cargo-* binaries in PATH |
Mechanism: .cargo/config.toml and PATH
# .cargo/config.toml
[alias]
b = "build"
mycommand = ["run", "--bin", "mytool"]
Custom subcommands: Any cargo-foo binary in PATH becomes cargo foo.
Documentation: https://doc.rust-lang.org/cargo/reference/config.html
No plugin or hook system for extending pip/uv behavior.
Documentation:
| Hook | Description |
|---|---|
| Plugins | Extend Poetry functionality |
Mechanism: Poetry plugins (Python packages)
from poetry.plugins.plugin import Plugin
class MyPlugin(Plugin):
def activate(self, poetry, io):
pass
Documentation: https://python-poetry.org/docs/plugins/
| Hook | Description |
|---|---|
| Bundler plugins | Extend Bundler functionality |
Mechanism: Plugins via gems
# Register hooks
Bundler::Plugin.add_hook('after-install') do |deps|
puts "Installed: #{deps.map(&:name)}"
end
Documentation: https://bundler.io/guides/bundler_plugins.html
| Hook | Description |
|---|---|
| Composer plugins | Extend Composer |
Mechanism: Packages of type composer-plugin
class MyPlugin implements PluginInterface, EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
'post-install-cmd' => 'onPostInstall',
];
}
}
Documentation: https://getcomposer.org/doc/articles/plugins.md
| Hook | Description |
|---|---|
| Extensions | Modify Maven core |
settings.xml profiles | Global configuration |
Mechanism: .mvn/extensions.xml
<extensions>
<extension>
<groupId>com.example</groupId>
<artifactId>my-extension</artifactId>
<version>1.0</version>
</extension>
</extensions>
Documentation: https://maven.apache.org/guides/mini/guide-using-extensions.html
| Hook | Description |
|---|---|
| Init scripts | Run before every build |
| Settings plugins | Modify settings |
| Convention plugins | Reusable build logic |
Mechanism: ~/.gradle/init.gradle or ~/.gradle/init.d/*.gradle
// ~/.gradle/init.gradle
allprojects {
repositories {
mavenLocal()
}
}
Documentation: https://docs.gradle.org/current/userguide/init_scripts.html
| Hook | Description |
|---|---|
APT::Update::Pre-Invoke | Before update |
APT::Update::Post-Invoke | After update |
DPkg::Pre-Invoke | Before dpkg runs |
DPkg::Post-Invoke | After dpkg runs |
Mechanism: /etc/apt/apt.conf.d/
// /etc/apt/apt.conf.d/99custom
DPkg::Post-Invoke { "echo 'dpkg finished'"; };
Documentation: https://manpages.debian.org/stable/apt/apt.conf.5.en.html
| Hook | Description |
|---|---|
| Plugins | Extend dnf functionality |
| Transaction hooks | Before/after transactions |
Mechanism: Python plugins in /etc/dnf/plugins/ or /usr/lib/python*/site-packages/dnf-plugins/
Documentation: https://dnf.readthedocs.io/en/latest/api_plugins.html
| Hook | Description |
|---|---|
| Alpm hooks | Run scripts on file triggers |
Mechanism: /etc/pacman.d/hooks/*.hook
[Trigger]
Type = File
Operation = Install
Target = usr/lib/modules/*/vmlinuz
[Action]
When = PostTransaction
Exec = /usr/bin/mkinitcpio -P
Documentation: https://man.archlinux.org/man/alpm-hooks.5
| Hook | Description |
|---|---|
| Taps | Third-party formula repos |
| External commands | brew-* executables in PATH |
Mechanism: Any brew-foo script in PATH becomes brew foo.
Documentation: https://docs.brew.sh/External-Commands
| Hook | Description |
|---|---|
| Overlays | Modify package set |
| Flakes | Composable configurations |
Mechanism: ~/.config/nixpkgs/overlays/
# ~/.config/nixpkgs/overlays/my-overlay.nix
self: super: {
myPackage = super.myPackage.override { ... };
}
Documentation: https://nixos.wiki/wiki/Overlays
| Hook | Description |
|---|---|
| BuildKit frontends | Custom Dockerfile syntax |
| Registry webhooks | External notification |
Documentation:
| Hook | Description |
|---|---|
| Plugins | Extend Helm CLI |
| Post-renderers | Modify manifests before apply |
Mechanism: Plugins installed via helm plugin install
Documentation: https://helm.sh/docs/topics/plugins/
| Hook | Description |
|---|---|
| Provider plugins | Add resource types |
| External data sources | Call external programs |
Mechanism: Provider binaries in plugin cache
Documentation: https://developer.hashicorp.com/terraform/plugin
| Hook | Description |
|---|---|
bashrc hooks | Run before/after phases |
| Portage hooks | Global event hooks |
Mechanism: /etc/portage/bashrc and /etc/portage/env/
# /etc/portage/bashrc
post_src_install() {
einfo "Custom post-install hook"
}
Documentation: https://wiki.gentoo.org/wiki//etc/portage/bashrc
| Package Manager | Plugin System | Config Hooks | External Commands |
|---|---|---|---|
| npm | No | No | No |
| Yarn Berry | Yes | Yes | No |
| pnpm | Yes (.pnpmfile.cjs) | Yes (v10.8.0+) | No |
| Cargo | No | Yes (config) | Yes (cargo-*) |
| pip/uv | No | No | No |
| Poetry | Yes | No | No |
| Bundler | Yes | No | No |
| Composer | Yes | No | No |
| Maven | Yes (extensions) | Yes | No |
| Gradle | Yes | Yes (init scripts) | No |
| apt | No | Yes | No |
| dnf/yum | Yes | Yes | No |
| pacman | No | Yes (alpm hooks) | No |
| Homebrew | No | No | Yes (brew-*) |
| Nix | Yes (overlays) | Yes | No |
| Helm | Yes | No | No |
| Portage | No | Yes (bashrc) | No |
This document is released under CC0 1.0 Universal (Public Domain).
187 followers · starred Sep 2026