Back to feed

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.

2. 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.

3. 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.

4. 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.

5. 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

6. 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.

7. 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 |
                ||     ||

            

8. 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.