Ubuntu: Installing debian package without sudo privileges
In Ubuntu, the standard way to install software is via the Debian package manager.
You download a .deb file, run sudo apt install <package_name>.deb or dpkg -i <package_name>.deb, and the app is available to everyone on the computer.
But what if you are a standard user, without any admin privileges, trying to install a debian package or install any application with apt install.
When sudo access is unavailable
In many real-world environments, users do not have access to
sudo. This is common on corporate-managed laptops,
shared servers, university lab machines, CI runners, and multi-tenant systems where users
are intentionally restricted from performing system-wide changes.
The primary reason for this restriction is system stability. Modern Linux systems rely on shared libraries that are used by multiple applications simultaneously. Upgrading or downgrading a library at the system level can unintentionally break other software that depends on a specific version.
Even though package managers attempt to handle compatibility, system-wide library changes are inherently risky on long-lived or shared systems. For this reason, administrators often deny sudo access entirely and require users to operate strictly within user-owned directories.
In such environments, the challenge is not installing software itself, but doing so without modifying shared system state.
Why dpkg and apt install software system-wide
Tools such as apt and
dpkg are designed to manage software at the
system level. When a package is installed, its files are placed into shared locations
such as /usr/bin,
/usr/lib, and
/etc.
In addition to copying files, the package manager updates the system package database
under /var/lib/dpkg and may execute maintainer
scripts that assume root privileges.
This is why commands like the following require
sudo by default:
sudo apt install package_name
sudo dpkg -i package_name.deb
Without elevated privileges, these tools cannot safely modify protected directories or maintain a consistent system-wide package state. This behavior is intentional and ensures that all users on the system see a predictable and stable environment.
Ways to install software without sudo
Although traditional package installation relies on system-wide package managers,
there are several well-established ways to install and run software entirely within
user-owned directories. These approaches avoid modifying shared system state and do
not require sudo access.
One common approach is to extract a .deb package
manually and place its contents under a directory owned by the user, such as
$HOME/.local. Since a Debian package is fundamentally
an archive, its binaries and libraries can often be executed directly once extracted.
Dependencies can be handled by downloading their corresponding
.deb files without installing them using
apt download. These packages can then be extracted
alongside the main application, allowing all required libraries to reside in user space.
At runtime, missing shared libraries can be resolved by adjusting
LD_LIBRARY_PATH or by rewriting binary metadata
using tools such as patchelf. This ensures the
application loads libraries from user-owned locations instead of system directories.
Alternative distribution formats such as AppDir and AppImage are designed with portability
in mind and can often be executed directly by unprivileged users. In some cases, building
software from source with a custom
--prefix=$HOME also provides a clean and isolated
installation.
Each method has trade-offs in terms of convenience, isolation, and maintenance, but all of them share the same goal: running software without altering the system-wide environment.
Understanding the structure of a .deb package
A Debian package (.deb) is not a binary installer,
but an archive(a standard Unix (ar) archive) containing both metadata and application files. Internally, it follows a
well-defined structure that allows package managers to install, remove, and track software
in a consistent manner.
A typical .deb file consists of three main components:
debian-binary
control.tar.xz
data.tar.xz
The debian-binary file specifies the package format
version. The control.tar.* archive contains metadata
such as package name, version, dependencies, and optional maintainer scripts. The actual
application files are stored inside data.tar.*.
During a normal installation, the package manager extracts the contents of
data.tar.* into system directories like
/usr and
/lib, and then executes maintainer scripts from the
control archive. These scripts frequently assume root privileges and may perform tasks such
as updating caches, creating users, or restarting services.
When installing without sudo, only the extraction of
data.tar.* is relevant. Metadata and maintainer
scripts are typically ignored, since the goal is not to register the package with the system
package manager, but to obtain runnable binaries and libraries.
This separation between metadata and payload is what makes user-space installation possible in the first place. As long as the application does not rely on privileged setup steps, its files can often be used directly after extraction.
A .deb package can simply be extracted using the cli utility ar.
This is provided by binutils
ar -x <package_name>.deb
Installing a .deb package without sudo
Installing a Debian package without sudo revolves
around one core idea: treating the .deb file as a
simple archive and manually reproducing only the parts of installation that are strictly
necessary to run the application.
Instead of registering the package with the system package manager, we extract its files into a user-owned directory, handle dependencies locally, and configure the runtime environment so the application can locate its binaries and shared libraries.
6.1 Extracting the Debian package
The easiest way to extract a .deb file is using
dpkg. This does not require root privileges and
simply unpacks the application files without touching the system.
mkdir -p $HOME/apps/myapp
dpkg -x myapp_1.0.0_amd64.deb $HOME/apps/myapp
After extraction, the directory structure usually mirrors a system installation. For example,
binaries may appear under usr/bin and libraries under
usr/lib relative to the extraction directory.
6.2 Inspecting package dependencies
Since we are bypassing apt install, dependencies
will not be resolved automatically. To identify required packages, inspect the package
metadata using:
dpkg -I myapp_1.0.0_amd64.deb
Look for the Depends field. These dependencies are
typically shared libraries or runtime components that the application expects to find on
the system.
6.3 Downloading dependencies without installing them
Even without sudo access, apt can still be used
to download package archives. The following command fetches the dependency
.deb file without installing it:
apt download libexample1
Each downloaded dependency can be extracted using the same
dpkg -x approach and placed alongside the main
application directory. This process may need to be repeated recursively if dependencies
themselves have additional runtime requirements.
6.4 Adding extracted binaries to PATH
Once binaries are extracted, they need to be discoverable by the shell. This can be achieved
by updating the PATH environment variable:
export PATH="$HOME/apps/myapp/usr/bin:$PATH"
For a persistent setup, this export can be added to shell initialization files such as
~/.bashrc or
~/.zshrc.
6.5 Resolving shared library dependencies
Even with binaries available in PATH, execution may
fail due to missing shared libraries. To diagnose this, use
ldd on the binary:
ldd $HOME/apps/myapp/usr/bin/myapp
Any library marked as not found must be provided
locally. Extract the corresponding dependency package and ensure its
.so files are accessible.
A quick solution is to prepend a custom library directory to
LD_LIBRARY_PATH:
export LD_LIBRARY_PATH="$HOME/apps/myapp/usr/lib:$LD_LIBRARY_PATH"
This approach is sometimes referred to as the ldd hack, as it relies on runtime linker behavior rather than modifying the binary itself. While effective, it can be fragile if multiple applications require conflicting library versions.
A more robust alternative is embedding a runtime library path directly into the binary
using tools like patchelf. This avoids global
environment variables and makes the application more self-contained.
patchelf --set-rpath '$ORIGIN/../lib' $HOME/apps/myapp/usr/bin/myapp
Here, $ORIGIN resolves to the directory containing
the binary, allowing libraries to be located relative to the executable location.
6.6 Known limitations
Not all dependencies can be safely bundled. Core components such as
glibc, kernel interfaces, or system services cannot
be overridden without risking instability. Applications that rely heavily on such components
may not be suitable for user-space installation.
Despite these limitations, many CLI tools and desktop applications can be installed and run successfully using this approach, making it highly valuable in restricted environments.
Walkthrough: installing a .deb package without sudo
To make the concepts discussed so far more concrete, let’s walk through installing a
simple Debian package without using sudo.
This walkthrough focuses not only on installation, but also on diagnosing and fixing
dependency-related failures.
Step 1: Download the package
apt download cowsay
This downloads the .deb archive without
installing it system-wide.
Step 2: Inspect declared dependencies from control metadata
Before extracting the package, it is useful to inspect the dependency metadata
declared by the maintainer. This information lives inside
control.tar.xz and can be queried directly:
dpkg -I cowsay_*.deb
Pay close attention to the Depends field.
These are the packages the application expects to be present at runtime. Since we
are not using apt install, these dependencies
must be handled manually if they are not already available on the system.
If deeper inspection is needed, the control archive itself can be extracted:
dpkg -e cowsay_*.deb control
This creates a control directory containing
files such as control,
postinst, and
prerm. Only the dependency metadata is relevant
for user-space installation; maintainer scripts are typically ignored.
Step 3: Extract the package into a user-owned directory
mkdir -p $HOME/apps/cowsay
dpkg -x cowsay_*.deb $HOME/apps/cowsay
Step 4: Add the binary location to PATH
export PATH="$HOME/apps/cowsay/usr/bin:$PATH"
Attempt to run the application:
cowsay "Hello from user space"
Step 5: Diagnose missing dependencies when execution fails
If the program fails to start, the most common cause is missing shared libraries.
Use ldd to inspect the binary:
ldd $HOME/apps/cowsay/usr/bin/cowsay
Any library marked as not found must be
provided manually. This is where the earlier dependency metadata becomes useful:
it tells you which packages to download next.
Step 6: Download and extract missing dependencies
apt download libtext-charwidth-perl
dpkg -x libtext-charwidth-perl_*.deb $HOME/apps/cowsay
Repeat this process until ldd reports no
missing libraries.
Step 7: Configure runtime library lookup
If extracted libraries are not in standard locations, configure the dynamic linker to find them:
export LD_LIBRARY_PATH="$HOME/apps/cowsay/usr/lib:$LD_LIBRARY_PATH"
Alternatively, for a more self-contained setup, embed a runtime search path directly
into the binary using patchelf.
Step 8: Make the setup persistent
export PATH="$HOME/apps/cowsay/usr/bin:$PATH"
export LD_LIBRARY_PATH="$HOME/apps/cowsay/usr/lib:$LD_LIBRARY_PATH"
With these steps completed, the application runs entirely from user space, without registering itself with the system package manager or modifying shared directories.
Step 9: Running the command again
vignesh@ubuntu-vm$ cowsay "Hello from user space"
_______________________
< Hello from user space >
-----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Final thoughts
Installing software without sudo is not about
bypassing system protections, but about working within environments where system-wide
changes are either restricted or undesirable. Debian-based systems enforce this
separation intentionally to preserve stability and consistency for all users.
Tools like apt and
dpkg are designed to manage shared state.
When sudo access is unavailable, treating a
.deb file as an archive and managing its contents
manually becomes a practical and controlled alternative.
This approach does come with trade-offs. Dependency resolution becomes a manual process, updates are no longer automatic, and some system-level dependencies cannot be safely overridden. For applications that rely heavily on core libraries or system services, a user-space installation may not be feasible.
However, for many CLI tools and self-contained applications, this method offers a safe, reproducible, and non-invasive way to run software in restricted environments. It also encourages a deeper understanding of package structure, dynamic linking, and runtime behavior — knowledge that remains valuable far beyond this specific use case. When sudo is not an option, understanding the mechanics beneath package management often matters more than the package manager itself.