If you use a PC or smartphone, you’ve probably bumped into these little mysteries more than once.
- Slack and VS Code only work after you install them, but Gmail and Google Docs run the moment you open a URL ─ what’s the difference?
- The world is full of “install” commands ─
pip(Python),npm(Node.js),brew(Mac),apt(Linux),winget(Windows). What are all of those actually doing? - Why do you need to type your admin password every time you install something?
- On macOS you usually drag a
.appinto a folder and you’re done, while Windows.exeinstallers walk you through dialog after dialog. Why the gap?
“Installation” is a single word that hides a lot of different feelings, which makes it hard to summarize. But once you trace each one back to its root, they all share the same answer.
For software to run, it has to formally introduce itself to the OS (Windows, macOS, Linux, etc.) ─ “Hi, a new app is moving in” ─ and shake hands with it.
“Installation” is just the name we give that handshake ceremony. In this article we’ll unpack:
- What is actually inside an executable file (
.exe/.app/.deb) - Why programs cannot run on their own and always need help from the OS
- What the “installation” black box is concretely doing behind the scenes
- Why “no installation needed” worlds (web apps, portable apps, containers) can exist at all
We’ll take it step by step with diagrams, assuming zero prior background.
We previously published What Is an IP Address? If network “addresses” are the foundation on the internet side, the OS-and-app handshake covered here is the foundation on the device side. Understanding both gives you a connected picture of how data reaches your machine and how the app on it actually runs.
1. What programs actually are ─ executables as instructions for the OS and CPU
What exactly is in that .exe, .app, or .deb “package” you just downloaded? Once we crack that open, it becomes clear why programs cannot run on their own.
1-1. An executable is just bytes ─ but bytes in a very specific order
If you peek at an executable, all you see is a long stream of zeros and ones (binary). It looks unreadable in a text editor. But the order of those bytes follows strict rules, and when the OS reads them, meaning emerges: “the program body starts here,” “this region is for data,” “this section lists external libraries,” and so on.
This OS-readable layout is called an executable file format. Each major OS uses one of three formats, and every PC in the world uses one of them.
| OS | Executable format | Common file extensions |
|---|---|---|
| Windows | PE (Portable Executable) | .exe / .dll |
| macOS | Mach-O (Mach Object) | no extension / executables inside .app bundles |
| Linux | ELF (Executable and Linkable Format) | no extension |
So when a Windows .exe refuses to run on a Mac, the reason isn’t malice ─ the OS just doesn’t speak the same instruction-format dialect. It’s a bit like sending a Japanese letter to a French reader: they can see the characters, but they can’t follow the meaning.
1-2. The four main parts inside an executable
The three formats differ in detail, but their high-level structure is similar. Splitting it into four “rooms” makes things much easier to picture.
┌─────────────────────────────────────┐
│ Header │ ← cover page the OS reads first ("ELF v1.0", "64-bit", target OS)
├─────────────────────────────────────┤
│ Code (.text) │ ← the CPU instructions themselves
├─────────────────────────────────────┤
│ Data (.data / .rodata) │ ← strings, constants, embedded resources
├─────────────────────────────────────┤
│ Symbols / link info │ ← list of "which external library and which function I'd like to call"
└─────────────────────────────────────┘
- The header is the cover page. The OS reads it first and decides “is this even a kind of file I can run?”
- Code holds the actual CPU instructions ─
add,mov,jmp, and so on. - Data stores the program’s raw materials: text strings, numerical defaults, image resources.
- Symbols / link info is a “shopping list of missing parts” ─ “I want to call
printfhere, please connect it for me,” it tells the OS and the loader.
See the picture? An executable is not a finished product. It’s a bag containing assembly instructions + raw materials + a shopping list. The OS reads the instructions, hands the code to the CPU, and wires up the missing parts (shared libraries) to make it actually run.
Don’t worry if the words “symbol” and “link info” sound intimidating. Just translate them as symbol (a label attached to a named function or variable inside the program) and link (the act of wiring up missing parts either at build time or at run time). We won’t go deeper than that here.
2. Programs do not run alone ─ the constant conversation with the OS
As §1 showed, an executable carries a “shopping list.” Where does it shop? The OS and its standard libraries. Whenever an app prints a character on screen, opens a file, or talks to the network, it isn’t doing the work itself ─ it’s politely asking the OS to do it.
2-1. The four-layer stack: app → standard library → kernel → hardware
Roughly speaking, the inside of a PC stacks up like this.
┌─────────────────────────────┐ ← Slack, VS Code, the apps you launched │ Application │ ├─────────────────────────────┤ │ Standard library │ ← C standard library, .NET, libc, Foundation, etc. │ (language runtime) │ The translation layer between app and OS ├─────────────────────────────┤ │ OS kernel │ ← Windows kernel, Linux kernel, Darwin │ │ Master of screen, files, network, memory ├─────────────────────────────┤ │ Hardware │ ← CPU, memory, SSD, GPU, network card └─────────────────────────────┘
When an app wants to “show ‘Hello’ on screen,” it doesn’t fire signals at the monitor itself. A relay race happens:
- The app calls a function such as
printf("Hello") - The standard library (libc on C, BCL on .NET) prepares a request to the OS for “screen output”
- It hands that request to the OS kernel ─ this hand-off is called a system call (an official “work order” from the app to the OS)
- The kernel goes through the display driver and tells the hardware what to render, and the letters appear
In other words, apps are written from the start with the assumption that the OS will help them. There’s no such thing as an executable that runs without help from an OS. That’s the deep reason “you install an app for a specific OS” is even a concept.
2-2. System calls ─ the official “order forms” to the OS
A system call (the dedicated counter where apps say “please do this” to the OS) typically asks for things like:
| What the app wants | Representative system call (Linux name) | What happens |
|---|---|---|
| Open a file | open() | The OS talks to the SSD, then returns a “ticket” (file descriptor) if it can be opened |
| Get more memory | mmap() / brk() | The OS slices off some free memory and assigns it to the app |
| Connect to the network | socket() / connect() | The OS drives the NIC and tries to reach the remote host |
| Exit | exit() | The OS cleans up (frees memory, etc.) and ends the process |
Each one is a job the app cannot do, or is not allowed to do, on its own, and so it asks the OS instead. Without the OS’s help, an app cannot draw a single character on screen.
The “installation” we explore in §3 boils down to “setting things up so the app can place these orders correctly with the OS”. Keep this §2 picture in mind and §3 will land much more naturally.
3. [Core] What “install” actually does behind the scenes
By now we know “an app cannot run without the OS’s help.” That makes installation easy to summarize: it is the umbrella term for the prep work that brokers a relationship between the app and the OS. Almost every installer in the world (.msi, .pkg, .dmg, .deb, .rpm, the .app drag on macOS, the install button in Microsoft Store, etc.) walks through the following six phases.
3-1. The six phases an installer runs through
Each phase takes a few hundred milliseconds to tens of seconds, and the sum of them is what you experience as “the install wait time.”
- 1Signature checkVerifies the file is “really from a legitimate author” (think of it as a stamp/seal check). On Windows it’s Authenticode, on Mac it’s Gatekeeper, on Linux it’s GPG signing on packages.
- 2ExtractionUnpacks the single bag (the installer) into the actual main binary, support libraries, configs, docs, etc. Up to here it is just “taking the materials out.”
- 3PlacementMoves the unpacked files into the OS’s standard locations. On Win that’s
C:\Program Files\, on Mac/Applications/, on Linux/usr/lib/and/usr/bin/. - 4RegistrationAdds the app’s name to the OS’s “address book.” Windows writes to the registry (the giant scratchpad where the OS keeps settings), Mac writes plist files, Linux drops a
.desktopfile. - 5IntegrationAdds icons to the Start menu / Launchpad / app menu, and associates the app with the relevant file extensions (
.txt,.psd, …). - 6Post-setupRegisters any background services, builds first-run caches, adjusts
PATH, creates shortcuts, and finally cleans up temp files.
Mnemonic: verify → extract → place → register → integrate → post-setup. Every installer takes you through these six steps to get from “downloaded” to “ready to use.”
3-2. Before / After ─ the filesystem visibly changes
If the “six phases” still feel abstract, comparing the disk before and after the install makes it immediate.
Right after download
~/Downloads/ └── SuperApp-1.2.3.exe ← single bag
After installing
C:\Program Files\SuperApp\ ├── SuperApp.exe ← main binary ├── core.dll, render.dll, ... ← parts (shared libraries) └── resources\ ← images, translations C:\Users\me\AppData\Roaming\SuperApp\ └── settings.json ← user settings Registry HKLM\Software\SuperApp ├── InstallPath = "C:\Program Files\SuperApp" └── Version = "1.2.3" Start Menu → SuperApp (shortcut)
As you can see, installation is the combined act of “splitting one .exe into the right folders + telling the OS about it.” It’s not that “double-click won’t run anything” ─ rather, in the seconds-to-minutes after the double click, a great deal of OS-level setup is happening. That’s the more accurate mental model.
You may have heard “moving the install to a different drive makes things lighter,” but Windows’ C:\Program Files\ and macOS’s /Applications/ are baked into many apps’ assumed locations. Forcing a custom path can break OS integration (§5) so badly the app refuses to launch. Sticking with the default path is almost always the right call.
4. Dependencies ─ why a single executable does not stand alone
“It’s just one app, why are there hundreds of files?” The answer lives in this section. The key is the concept of shared libraries (parts boxes that many apps borrow from).
4-1. What a shared library is
“Save this image as JPEG” is the kind of feature Photoshop, the VS Code extensions, and Slack all want at some point. If every app shipped its own copy:
- The disk balloons with duplicate parts
- A single security fix forces every app to be re-released
To avoid that pain, the OS provides a way to collect common parts in one file and “lend them out” to multiple apps. That’s a shared library (extension .dll on Win, .dylib on Mac, .so on Linux).
┌──────────────────────────┐
│ your application │
└──────────────────────────┘
│ │ │
┌──────────────┘ │ └──────────────┐
▼ ▼ ▼
libjpeg.dll libssl.dll libcurl.dll
(JPEG parts box) (crypto parts box) (HTTP parts box)
│ │ │
└──────────────────┴──────────────────┘
▼
┌──────────────────────┐
│ OS API (kernel) │
└──────────────────────┘
Even if the app itself is only a few MB, in practice it depends on a handful of these “parts boxes,” and the installer drops them onto the disk along with the main binary. That’s why “one app” can take up hundreds of MB once installed.
4-2. DLL Hell ─ when shared parts start fighting over versions
Shared libraries are practical, but they have a famous failure mode: if versions don’t line up, everything goes sideways at once. This is the legendary DLL Hell (the chaos when two apps demand different versions of the same parts box and end up breaking each other).
App A ─→ requires libfoo v1.0
App B ─→ requires libfoo v2.0
│
▼
Only one libfoo can sit in the OS
→ one of them is guaranteed to break
Modern operating systems mitigate this in different ways:
- Windows:
WinSxS(Side-by-Side) andMicrosoft.UI.Xaml-style packages let multiple versions co-exist under different file names - macOS: encourages each
.appto bundle its own copy of the.dylibs it needs - Linux: package managers (covered later) resolve the dependency graph for you
Even so, when you dig out an old piece of software and see MSVCR100.dll not found, that error is the lingering shadow of this very problem.
“So why don’t we just ship a single self-contained binary that includes everything?” That’s exactly the philosophy behind modern Go binaries and Electron apps. The trade-off is bigger disk usage. Remember the choice as “share to save space, or self-contain to stay stable”.
5. OS integration ─ file associations, menu entries, autostart
In phases 4 and 5 of §3 (registration and integration), the installer makes the app officially announce itself to the OS. Looking at exactly “what gets written where,” side by side across operating systems, makes the differences obvious.
5-1. What gets registered, OS by OS
| What is registered | Windows | macOS | Linux |
|---|---|---|---|
| Install ledger | Registry (HKLM\Software\<App> ─ the giant scratchpad where the OS keeps settings) | plist (/Library/LaunchAgents/*.plist ─ the macOS equivalent scratchpad) | .desktop file (/usr/share/applications/*.desktop ─ the Linux equivalent scratchpad) |
| Menu entry | Start menu | Launchpad (auto-collected) | App menu (auto-built from .desktop files) |
| File associations | Registry HKCR\.psd → Photoshop.exe | CFBundleDocumentTypes in Info.plist | mimeapps.list |
| Autostart | Startup folder / HKCU\...\Run / Task Scheduler | LaunchAgents / LaunchDaemons (plist) | systemd units / autostart/*.desktop |
| Background services | Service Manager (services.msc) | LaunchDaemons (plist) | systemd units |
“Apps may look the same on the surface, but the scratchpad they actually write into is different on every OS.” Once that clicks, it’s no surprise that “the software ran fine on Windows but feels like a different beast on macOS.”
5-2. Why “just dropping the executable somewhere” is not enough
You can leave something like notepad.exe in Downloads and double-click it; technically it’ll run on its own. But you won’t get experiences like:
- Quick launching from the Start menu
- Double-clicking
.txtopening it in that app automatically - One-click uninstall from the OS uninstall screen
- Automatic updates running on their own
- The same icon being there after a reboot
All of those are the fruit of OS integration. Installation became a “place files + register them with the OS” combo precisely to enable that experience.
6. Permissions and security ─ why it asks for your admin password
“Why does it ask for my password every single time I install something?” That little bit of friction is actually proof that the OS’s safety latch is working. Once you understand the reasoning, the question stops feeling annoying.
6-1. System area vs. user area
An OS’s filesystem is split into places that require admin rights to write and places that don’t.
┌──────────────────── Admin rights required ──────────────────┐ │ Windows : C:\Program Files\, C:\Windows\, HKLM\registry │ │ macOS : /Applications/, /Library/, /usr/local/ │ │ Linux : /usr/, /etc/, /opt/ │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────── User rights are enough ─────────────────┐ │ Windows : %USERPROFILE%\AppData, HKCU\registry │ │ macOS : ~/Library/, ~/Applications/ │ │ Linux : ~/.local/, ~/.config/ │ └──────────────────────────────────────────────────────────────┘
During phase 3 (placement) of §3, an installer that wants to drop the app into the system area will be stopped by the OS asking, “are you sure you want this?” That’s exactly:
- The Windows UAC dialog (“Do you want to allow this app to make changes to your device?”)
- The macOS password prompt
- The Linux
sudopassword challenge
And the symmetric case ─ “drag a Mac .app into your personal ~/Applications/ and no password is asked” ─ falls out of the same logic.
6-2. Signature verification ─ the stamp-of-authenticity check
On top of permissions, the OS also pre-checks “is this installer really from the author it claims to be?” This is signature verification (the “stamp/seal” check that confirms the installer is the real thing from a legitimate author), and each OS has its own version:
| OS | Mechanism | What is checked |
|---|---|---|
| Windows | Authenticode / SmartScreen | Digital signatures on .exe / .msi |
| macOS | Gatekeeper / Notarization | Apple Developer ID signatures on .app / .pkg |
| Linux | Package manager GPG keys | Publisher signatures on .deb / .rpm |
Warnings like “this app is from an unidentified developer” or “this app cannot be opened because it could not be verified” are exactly the result of these signature checks. Failing the check doesn’t automatically mean malware ─ small independent developers often skip code-signing because certificates are expensive ─ but “don’t run anything that fails the check” is the safe default.
When you say “security,” you may also be thinking of sandboxes (the mechanism that traps an app inside a “private room” away from the rest of the system). A sandbox is protection at run time; signature verification is protection at install time. They live in different layers, and you need both. Sandboxes connect directly to the next section’s containers and web apps.
7. The “no installation needed” world
So far we’ve seen “installation = handshake with the OS.” If something else does that handshake on your behalf, then in principle you don’t need to install anything yourself. And in practice, modern computing has multiple ways to do exactly that. Let’s quickly see how each one pulls it off.
7-1. Four approaches at a glance
| Approach | Launch prep | OS integration | Isolation | Ease of uninstall | Examples |
|---|---|---|---|---|---|
| Web app | Just open a URL | None (runs inside the browser) | High (browser sandbox) | Close the tab | Gmail, Google Docs, Figma |
| Portable app | Unzip and launch | Almost none (no file associations or menu entries) | Low (same as a normal app) | Just delete the folder | Sysinternals, the portableapps.com family, some OSS tools |
| Container | Launch with docker run etc. | None (runs in a sealed box separate from the host OS) | High (files and network are confined to the box) | Delete the container | Docker, Podman, apps on Kubernetes |
| Package manager | One command line | Yes (the §3 six phases run automatically under the hood) | Same as a regular install | One command | apt, brew, winget, pip, npm |
| Native install (for reference) | Click through the installer | Full | Low | Control Panel etc. | .msi, .pkg, .dmg |
7-2. Why each approach gets away with “no install”
Each one explained in plain terms, with a beginner-friendly analogy attached.
- Web app: the actual program lives on a remote server, not on your PC. Your PC is only running a browser. Because the browser itself has already shaken hands with the OS, the apps that ride on top of it don’t need their own handshake (think of it as borrowing a table at a restaurant).
- Portable app (think of it as the whole app squeezed onto a USB stick): it carries all the parts it needs and avoids touching the registry / plist, so unzipping is enough. The trade-off is bigger size and no OS integration (no menu entries, no file associations).
- Container (think of it as a sealed, portable little room that holds the app together with the bits of OS it needs): not just the app, but the libraries and config it relies on are packed into the room and run isolated from your host OS. The handshake takes place inside the room, so almost nothing is written to your actual PC. This is exactly the convenience that made “ship the whole runtime, not just the code” the cloud-era default.
- Package manager (it automates the “install” routine that you’d otherwise do manually, on the OS’s behalf): it looks like “no installation,” but really it’s just running the §3 six phases for you with a single command. It’s better thought of as “installation, but well-automated.” During the few seconds of
brew install nginx, the verify / extract / place / register dance is happening behind the curtain.
Once you see this, “do I need to install or not?” reduces to “who is doing the OS handshake on my behalf?” For web apps, the browser; for containers, the container engine; for package managers, an automation script. Someone is always doing the handshake somewhere.
Even with Docker, there’s a layered structure: Docker itself has to be installed onto your OS once, but every app you run on it then arrives “without being installed on your PC.” “No install” never holds for the entire world ─ somewhere, someone has shaken hands once.
8. See it in practice ─ commands that show what installation actually changed
Now that the theory is clear, let’s open up your own PC and confirm “yes, files really do get added during installation.” Each OS has a one or two line command for this.
8-1. List installed apps / packages
First, let’s print “what is currently installed on this PC” per OS.
# Windows (PowerShell)
Get-Package | Select-Object Name, Version | Sort-Object Name
# macOS (apps installed via Homebrew)
brew list --versions
# Linux (Debian / Ubuntu)
dpkg -l | head -20
8-2. Inspect what files a single app put down
You can also drill into a single package and see exactly which files its installer scattered across the system.
# Linux (Debian / Ubuntu) ─ list every file the git package placed
dpkg -L git | head -20
# macOS ─ inspect a package installed via the official .pkg path
pkgutil --pkg-info com.apple.pkg.CLTools_Executables
# Windows ─ list installs via the registry Uninstall keys
Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*' | Select-Object DisplayName, DisplayVersion
You’ll see entries like /usr/bin/git, /usr/share/man/man1/git.1.gz, /etc/bash_completion.d/git ─ in other words one app spreading itself across many places in the OS. Realizing “that one downloaded file actually seeps into the OS this widely” is what makes installation feel real instead of magical.
Wrap-up ─ four lines that capture it all
Long article, but its essence fits in four bullet points.
- An app cannot run alone. It runs only by borrowing the OS and the hardware ─ which is why a handshake protocol with the OS is unavoidable.
- Installation is the ceremony that performs that handshake ─ verify, extract, place, register, integrate, post-setup, six phases in order.
- The admin password is “are you sure you want to write into the system area?” The signature check is “is this really from a legitimate author?”
- Web / portable / container / package manager are all “proxies” for the handshake ─ “no install” only works because someone, somewhere, has already shaken hands.
With these four in mind, you can read .exe, .app, .deb, or docker run through the same lens and understand what is happening in front of you.
For the network-side foundation, see our previous What Is an IP Address?. Combine it with today’s “OS-and-app handshake” and you have the full picture of how data reaches your device and how the app on it actually runs. To go deeper still, Choosing the Right Numeric Type in SQL on this site walks the same kind of “how data is represented inside a program” thread.
FAQ
Q1. What is the difference between a portable app and an installed app?
A. The actual binary inside is almost the same; what differs is whether OS integration happens. An installed copy runs all six phases of §3 and “shakes hands deeply” with the OS, so you get Start menu entries, file associations, automatic updates, and so on. A portable copy stops at phases 1–2 (verify, extract) plus the placement of the folder ─ it deliberately skips having its name added to the OS’s address book.
Q2. Does uninstalling really remove everything?
A. Most of it disappears, but settings and user data are usually left behind. Windows leaves config under C:\Users\<you>\AppData\, Mac under ~/Library/Application Support/<App>/, Linux under ~/.config/<app>/ ─ on purpose, so a future re-install can pick up your preferences. If you want the full nuke, delete those folders manually or use a helper such as AppCleaner (Mac) or Bulk Crap Uninstaller (Windows).
Q3. Why does drag-and-drop work for macOS .app bundles?
A. A .app is not really a “file” ─ it’s a folder packed with everything the app needs (we call this a bundle). The main binary, the parts (.dylib), the resources, and Info.plist are all inside, so very little registry-style external editing is needed. Dropping the folder into /Applications/ is enough for the OS to recognize it. Windows .exe binaries can be run standalone too, but the Windows convention scatters their settings into the registry, so a single drag isn’t enough to leave a tidy install.
Q4. Does running Docker count as “installing”?
A. Think of it as two layers. Docker itself (the engine that runs containers) does need a one-time native install on your PC. Once it’s there, every individual app you run on top of it lives inside a container (“private room”) and never gets written into your real OS. “Apps that look install-free” are install-free precisely because the Docker engine is doing the OS handshake on their behalf.
Q5. Can I install software without admin rights?
A. Yes, as long as you stay in the user area. On Windows there are “user installers” that drop into %LOCALAPPDATA% (VS Code’s “User Installer,” for example), on Mac you can drag a .app into ~/Applications/, and on Linux you can copy binaries into ~/.local/bin/ or use pipx install --user. But anything that affects the whole system (driver installation, persistent background services, dropping files into /usr/local/, and so on) inherently needs to write into the system area, so admin rights become unavoidable.

Leave a Reply