Infra, Illustrated.
LINUX FOUNDATION · PACKAGES CONSOLE

Packages & Software

Follow a package from signed repository to unpacked files and runtime dynamic linking, and see why installing is not running.

← Back to Linux MapREFERENCE HOST · devbox-01DEEP DIVE · FOUNDATIONALPublished Sep 15, 2026 · Reviewed Sep 18, 2026
CHAPTER 05 / 08 · 7 VISUAL LESSONS

What this chapter builds

A package manager is a trust, dependency, filesystem, and lifecycle system. This chapter follows software from repository metadata to a running, linked service.

By the end, you can

  • Explain what repository signatures establish
  • Locate the files and scripts a package installed
  • Trace dynamic library resolution before main()
  • Separate installed, enabled, active, and listening states

Objects to keep straight

ELF interpretermemory mappingsfilesystem pathsservice cgroupsocket

Applied task: Find the first broken boundary when a package is installed but its service will not accept connections.

SCENARIO · devbox-01 / nginxCore concepts
OBJECT Core conceptsSTATE ready to inspectOWNER package manager / service manager
01 / PACKAGES · SYSTEM MODEL

Before looking at the mechanics, what actually is a Package or a Dependency?

What are the foundational primitives of Linux Package Management?

DEVBOX-01 · CORE CONCEPTSGLOSSARY

Package (.deb / .rpm)

Analogy: A Zip File with Instructions

A package is just a compressed archive of compiled binaries, config files, and a manifest. It also contains "Maintainer Scripts" that run as root before and after extraction.

Dependency

Analogy: A Prerequisite Course

Software relies on other software. If Nginx requires OpenSSL to handle HTTPS, OpenSSL is a dependency. The package manager downloads and installs these automatically.

Repository

Analogy: An App Store Server

A static HTTP server hosting thousands of packages and a cryptographically signed index file (Release/Packages). Your system checks this index to find what is available.

Shared Library (.so)

Analogy: A Shared Toolshed

Instead of every program containing its own copy of common code (like printing text), they use "Shared Objects" (.so files) stored in /usr/lib. Programs link to them at runtime.

Packages are just verified zip files. The package manager ensures all required dependencies are present before extracting them to the filesystem.

Linux software distribution is fundamentally decentralized. You do not download random installers from websites. Instead, your distribution (Ubuntu, RHEL) compiles the software, signs it with a cryptographic key, and hosts it on a Repository. Tools like APT or DNF download these packages, verify the signature, extract the files to standard locations (/usr/bin, /etc), and wire up Systemd services.

Model scope. Adding random PPA repositories or running "curl | sudo bash" bypasses this entire trust chain, granting unknown third parties root access to your machine.

INSPECT · dpkg -I /var/cache/apt/archives/nginx_1.22.1-9_amd64.deb
 new Debian package, version 2.0.
 Package: nginx
 Version: 1.22.1-9
 Architecture: amd64
 Depends: libc6 (>= 2.34), libpcre3, libssl3, zlib1g
 Description: small, powerful, scalable web/proxy server
NEXT PROBE · apt-cache depends nginx | grep "Depends:"
  Depends: libc6
  Depends: libcrypt1
  Depends: libpcre2-8-0
  Depends: libssl3
Primary source ↗
02 / PACKAGES · ARCHITECTURAL COMPARISON

A package manager is a cryptographically verified distribution pipeline.

Where do deb and rpm packages originate, and how does the system prove authenticity before execution?

DEVBOX-01 · ARCHITECTURAL COMPARISON4 ENTITIES COMPARED
01Debian / Ubuntu (APT)

Configured in /etc/apt/sources.list.d/. Validated via detached GPG signatures on InRelease metadata in /usr/share/keyrings/. Packages.gz supplies per-package SHA256 checksums.

$ apt-cache policy nginx
nginx:
  Installed: 1.22.1-9
  Candidate: 1.22.1-9
  Version table:
 *** 1.22.1-9 500
        500 http://deb.debian.org/debian bookworm/main amd64 Packages
02RHEL / Fedora (DNF/RPM)

Configured in /etc/yum.repos.d/*.repo. Validated via repomd.xml signatures; individual RPM package headers embed detached GPG signatures verified before unpacking.

$ dnf info nginx
Name         : nginx
Version      : 1.24.0
Release      : 1.el9
Architecture : x86_64
Repository   : @System
From repo    : epel
03GPG Keyring Security

Modern APT stores armored GPG public keys in /usr/share/keyrings/ rather than legacy trusted.gpg. The signed-by directive strictly limits key authority to a single repo.

$ gpg --show-keys /usr/share/keyrings/nginx-archive-keyring.gpg
pub   rsa2048 2011-08-19 [SC] [expires: 2027-04-12]
      573B FD6B 3D8F BC64 1079  A6AB ABF5 BD82 7BD9 BF62
uid   nginx signing key <signing-key@nginx.com>
04Maintainer Script Risk

Packages execute maintainer scripts (preinst, postinst, prerm, postrm) as root during installation with zero container sandboxing. Malicious PPAs can compromise the host.

$ head -n 15 /var/lib/dpkg/info/nginx.postinst
#!/bin/sh
set -e
if [ "$1" = "configure" ]; then
    if ! getent passwd nginx >/dev/null; then
        adduser --system --quiet --group --home /var/cache/nginx nginx
    fi
fi
Repositories are static HTTP mirrors; trust is anchored by local GPG keyrings and chained SHA256 checksums.

Package managers do not compile software; they download pre-built binary archives from static HTTP mirrors. Trust is established mathematically: the repository Release file is signed with a private GPG key, which matches a public key in your local keyring. The Release file lists SHA256 hashes for all package index files, which in turn specify hashes for each .deb archive. If a single byte is modified in transit, the package manager halts installation.

Model scope. Maintainer scripts (preinst/postinst) run as unrestricted root. Adding random PPAs or running "curl | bash" allows third parties to execute arbitrary code on your system.

INSPECT · apt-cache policy nginx
nginx:
  Installed: 1.22.1-9
  Candidate: 1.22.1-9
  Version table:
 *** 1.22.1-9 500
        500 http://deb.debian.org/debian bookworm/main amd64 Packages
NEXT PROBE · cat /etc/apt/sources.list.d/nginx.sources 2>/dev/null || cat /etc/apt/sources.list.d/nginx.list 2>/dev/null || grep -v "^#" /etc/apt/sources.list | head -n 3
deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/mainline/debian/ bookworm nginx
Primary source ↗
03 / PACKAGES · PIPELINE WALKTHROUGH

A package is an archive that unpacks into the filesystem hierarchy.

Where do binaries, configs, and unit files land when unpacking, and how does the database track ownership?

DEVBOX-01 · EXECUTION PIPELINESTAGE 01 / 04 ACTIVE
INTERACTIVE STAGE STEPPER

Stage 01: The package database records every file extracted into the Filesystem Hierarchy Standard (FHS). Binaries go to /usr/sbin, configs to /etc, and unit files to /lib/systemd/system.

$ dpkg -L nginx
/etc/nginx
/etc/nginx/nginx.conf
/lib/systemd/system/nginx.service
/usr/sbin/nginx
/usr/share/man/man8/nginx.8.gz

The package database records every file extracted into the Filesystem Hierarchy Standard (FHS). Binaries go to /usr/sbin, configs to /etc, and unit files to /lib/systemd/system.

Use dpkg -L to find where files landed, dpkg -S to identify file ownership, and dpkg -V to detect modified configurations.

Linux packages do not install into isolated application folders like macOS bundles. Instead, the archive extracts files across standard directories: executables into /usr/bin or /usr/sbin, configuration files into /etc, libraries into /usr/lib, and man pages into /usr/share/man. The local database (/var/lib/dpkg or /var/lib/rpm) stores a complete index of all extracted files and MD5 checksums.

Model scope. Files generated dynamically at runtime (access logs in /var/log, databases in /var/lib, cache in /var/cache) are not tracked by dpkg and remain on disk after "apt remove".

INSPECT · dpkg -S /usr/sbin/nginx
nginx: /usr/sbin/nginx
NEXT PROBE · dpkg -V nginx 2>/dev/null || rpm -V nginx 2>/dev/null
??5?????? c /etc/nginx/nginx.conf
Primary source ↗
04 / PACKAGES · SYSTEM MODEL

The dynamic linker binds shared ELF libraries at runtime before main().

Why does an installed binary fail with "error while loading shared libraries" even though the file exists?

DEVBOX-01 · SYSTEM MODELBLOCKED FIXTURE
1. EXECVEBinary ExecKernel reads ELF .interp
2. LOADERld-linux.soTakes initial CPU control
3. DT_NEEDEDDependencieslibssl.so, libc.so
4. CACHE LOOKUP/etc/ld.so.cacheFast hash cache lookup
5. MMAP & JUMPmain() EntrySegments mapped to memory
FAILURE / ALTERNATE STATE SIMULATION

Default fixture: Most Linux binaries are dynamically linked ELF executables. When executed, the kernel starts the dynamic linker (ld-linux.so), which parses the binary's DT_NEEDED ELF tags to find required shared libraries (.so files). The linker searches in a strict priority order: DT_RPATH/RUNPATH, LD_LIBRARY_PATH, the compiled binary cache in /etc/ld.so.cache, and standard system paths (/lib, /usr/lib). If any library is missing or incompatible, the process aborts with exit code 127 before main() ever executes.

Inspect dynamic library requirements with ldd or readelf -d; update the linker cache with ldconfig after installing custom libraries.

Most Linux binaries are dynamically linked ELF executables. When executed, the kernel starts the dynamic linker (ld-linux.so), which parses the binary's DT_NEEDED ELF tags to find required shared libraries (.so files). The linker searches in a strict priority order: DT_RPATH/RUNPATH, LD_LIBRARY_PATH, the compiled binary cache in /etc/ld.so.cache, and standard system paths (/lib, /usr/lib). If any library is missing or incompatible, the process aborts with exit code 127 before main() ever executes.

Model scope. ldd may execute untrusted binaries under certain linker configurations; for untrusted binaries, inspect ELF headers safely using "readelf -d" or "objdump -p".

INSPECT · ldd /usr/sbin/nginx
	linux-vdso.so.1 (0x00007ffe12345000)
	libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f1234000000)
	libpcre2-8.so.0 => /lib/x86_64-linux-gnu/libpcre2-8.so.0 (0x00007f1234100000)
	libssl.so.3 => /lib/x86_64-linux-gnu/libssl.so.3 (0x00007f1234200000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1234300000)
NEXT PROBE · readelf -d /usr/sbin/nginx | grep -E "(NEEDED|RPATH|RUNPATH)"
 0x0000000000000001 (NEEDED)             Shared library: [libcrypt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpcre2-8.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libssl.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
Primary source ↗
05 / PACKAGES · COMPARISON MODEL

Package installation extracts files; it does not guarantee a running process or socket.

Why does an application fail to respond immediately after running "apt install" or "dnf install"?

DEVBOX-01 · COMPARISON MODELREADY TO INSPECT
1. PACKAGE STATEdpkg / rpmFiles extracted on disk (/usr/bin)
2. SYSTEMD UNITsystemctlUnit enabled; multi-user.target
3. CGROUP PROCESStask_structPID 418 in system.slice cgroup
4. TCP SOCKETstruct sockKernel socket listening on :80/:443
Installing writes files to disk. Running requires systemd service activation and successful socket binding.

Installing software simply writes files into the filesystem hierarchy. Transforming those static files into an active, listening server requires distinct orchestration steps: the package manager must invoke systemd triggers, systemd must parse the unit file, spawn the executable in an isolated cgroup slice, and the application must successfully bind to its configured network sockets. On Debian/Ubuntu, packages attempt to start immediately upon install; on RHEL/Fedora, services default to disabled and stopped.

Model scope. If the default configuration contains an error or the target port is occupied, the package post-install script may fail, leaving the package in a half-configured state ("iF" in dpkg).

INSPECT · systemctl status nginx
● nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Wed 2026-09-16 10:00:00 UTC; 2h ago
   Main PID: 418 (nginx)
      Tasks: 2 (limit: 4915)
     Memory: 8.2M
        CPU: 120ms
     CGroup: /system.slice/nginx.service
             ├─418 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             └─421 "nginx: worker process"
NEXT PROBE · systemctl is-enabled nginx && systemctl is-active nginx
enabled
active
Primary source ↗
06 / PACKAGES · PIPELINE WALKTHROUGH

Package pinning protects production systems from unintended breaking upgrades.

How do you freeze critical database and runtime packages while applying security patches across the host?

DEVBOX-01 · EXECUTION PIPELINESTAGE 01 / 04 ACTIVE
INTERACTIVE STAGE STEPPER

Stage 01: Query repository version candidate table. We have 15.4 installed, while 15.6 is available in upstream repositories.

$ apt list -a postgresql-15
postgresql-15/stable 15.6-0+deb12u1 amd64 [upgradable from: 15.4-1]
postgresql-15/now 15.4-1 amd64 [installed,upgradable]

Query repository version candidate table. We have 15.4 installed, while 15.6 is available in upstream repositories.

Use apt-mark hold or /etc/apt/preferences.d/ pinning to prevent automated upgrades of stateful databases and critical runtimes.

Unattended upgrades are critical for host security, but automated major or minor version bumps can break database schemas or API contracts. Holding a package (or configuring APT Pin-Priority) instructs the resolver to freeze that specific package at its current version while continuing to patch the rest of the operating system.

Model scope. Held packages do not receive security updates. Track pinned CVEs in your vulnerability management pipeline and schedule explicit upgrade windows.

INSPECT · apt-mark showhold
postgresql-15
kubelet
kubeadm
kubectl
NEXT PROBE · dpkg --get-selections | grep hold
postgresql-15                                   hold
kubelet                                         hold
Primary source ↗
07 / PACKAGES · DIAGNOSTIC DECISION TREE

A systematic diagnosis tree to isolate runtime application failures.

When an application fails to start or respond after an update, how do you isolate where the failure occurred?

DEVBOX-01 · DIAGNOSTIC DECISION TREENAVIGATE DIAGNOSTIC BRANCHES
STARTstart
Service down / Unresponsive
QUESTIONq-pkg
Is package cleanly installed? (dpkg -s app)
ACTIONa-pkg
Resolve half-installed state: apt-get install -f
QUESTIONq-bin
Does the binary execute syntax check? (app -t / --version)
ACTIONa-ldd
Inspect missing libraries with ldd; run ldconfig or install deps
QUESTIONq-unit
Does systemd service start cleanly? (systemctl start app)
ACTIONa-journal
Read journalctl -u app.service -e for exact exit codes or missing dirs
QUESTIONq-port
Is app listening on configured network sockets? (ss -tulpn)
ACTIONa-bind
Check for port conflicts or loopback-only bind address (127.0.0.1)
RESULTr-ok
Service verified: package valid, binary linked, unit active, socket listening
Test from the inside out: Package database -> Binary dynamic linker -> Config syntax -> Systemd unit -> Network socket.

Troubleshooting software requires isolating system layers from the inside out: Package database -> Binary dynamic linking -> Configuration syntax -> Systemd unit manager -> Network socket binding. Blindly restarting services without checking these boundaries destroys ephemeral logs and masks the root cause.

Model scope. AppArmor and SELinux policies can silently block an otherwise valid binary from reading configuration files or binding ports. Check dmesg or audit.log for AVC denials.

INSPECT · nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
NEXT PROBE · systemctl status nginx --no-pager
● nginx.service - A high performance web server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running)
Primary source ↗