# Adding Custom apt, pip, or conda Packages via Install Lists in LabNow AI

> Easily add custom apt, pip, or conda packages to LabNow AI containers by editing plain text install lists and committing changes for automatic rebuilds. Streamline your AI environment setup.

- Repository: [LabNow.ai/lab-foundation](https://github.com/labnow-ai/lab-foundation)
- Tags: how-to-guide
- Published: 2026-03-05

---

**You can add custom system, Python, or Conda packages to LabNow AI containers by editing the plain-text install-list files in `docker_core/work/` and committing the changes to trigger an automatic rebuild via GitHub Actions.**

LabNow AI (`labnow-ai/lab-foundation`) uses a modular build system where container dependencies are declared in simple text files rather than embedded in Dockerfile `RUN` commands. By modifying these install lists and leveraging the helper utilities defined in [`docker_atom/work/script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/docker_atom/work/script-utils.sh), you can extend base images with any additional software without altering the core build logic.

## How Install Lists Work

The LabNow AI build pipeline separates package definitions from installation logic through a set of conventions and helper functions.

### The Core Architecture

The system relies on three main components:

- **Install-list files** — Plain-text files where each line specifies a package name. Files use extensions `.apt`, `.pip`, or `.conda` to indicate the package manager. Comments start with the `%` character.
- **Utility script** — [`docker_atom/work/script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/docker_atom/work/script-utils.sh) defines generic installer wrappers (`install_apt`, `install_pip`, `install_conda`, `install_mamba`) that parse these lists and invoke the appropriate package manager.
- **CI/CD Pipeline** — The [`.github/workflows/build-docker.yml`](https://github.com/labnow-ai/lab-foundation/blob/main/.github/workflows/build-docker.yml) workflow monitors these files and automatically rebuilds images when changes are detected.

### List Parsing Logic

At build time, the `install_apt()` function (line 6 of [`script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/script-utils.sh)) processes list files by stripping comments and passing clean package names to `apt-get`:

```bash
apt-get -qq update -yq --fix-missing \
&& apt-get -qq install -yq --no-install-recommends $(cat "$1" | cut -d '%' -f 1)

```

The `cut -d '%' -f 1` command removes any trailing comments, ensuring only the package name reaches the installer. Equivalent logic exists for `install_pip()` and `install_conda()` functions within the same file.

### Dockerfile Integration

The Dockerfiles invoke these helpers during the build phase. In `docker_core/Dockerfile`, the installation steps appear at lines 42 and 91:

```dockerfile
&& install_apt "/opt/utils/install_list_latex_${profile}.apt" \
&& install_pip "/opt/utils/install_list_PY_${profile}.pip"

```

After installation, the `list_installed_packages()` function (lines 60-72 of [`script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/script-utils.sh)) logs all installed components for verification.

## Step-by-Step: Adding Your Own Packages

### 1. Select the Correct Install List

Choose the file that matches your package type and target environment:

| Package Type | Example File Path | Location |
|--------------|-------------------|----------|
| System (`apt`) | `docker_core/work/install_list_PY_base.apt` | `docker_core/work/` |
| Python (`pip`) | `docker_core/work/install_list_PY_base.pip` | `docker_core/work/` |
| Conda packages | `docker_core/work/install_list_core.conda` | `docker_core/work/` |

The naming convention follows `install_list_<scope>.<ext>`, where `<ext>` is `apt`, `pip`, or `conda`.

### 2. Add Packages with Comments

Edit the chosen file and add one package per line. Append `%` followed by a description for inline documentation.

Adding `ffmpeg` via apt:

```text

# docker_core/work/install_list_PY_base.apt

ffmpeg               % Multimedia processing tools

```

Adding `numpy` via pip:

```text

# docker_core/work/install_list_PY_base.pip

numpy                % Fundamental package for scientific computing

```

Adding `scikit-learn` via conda:

```text

# docker_core/work/install_list_core.conda

scikit-learn         % Machine-learning toolkit

```

The first three lines of each template file contain format instructions for future reference.

### 3. Trigger the Build Pipeline

Commit your changes to the repository:

```bash
git add docker_core/work/install_list_PY_base.apt \
        docker_core/work/install_list_PY_base.pip \
        docker_core/work/install_list_core.conda
git commit -m "Add custom packages: ffmpeg, numpy, scikit-learn"
git push origin main

```

The GitHub Actions workflow defined in [`.github/workflows/build-docker.yml`](https://github.com/labnow-ai/lab-foundation/blob/main/.github/workflows/build-docker.yml) detects the file modifications and initiates a new image build automatically.

### 4. Verify Installation

Once the build completes, validate the packages exist in the new image:

```bash
docker run --rm labnow/lab-foundation:latest bash -c "\
  ffmpeg -version && \
  python -c 'import numpy, sklearn; print(numpy.__version__, sklearn.__version__)'"

```

For debugging during local development, use the `install_echo` helper to preview parsed package names without installing:

```bash
source /opt/utils/script-utils.sh
install_echo /opt/utils/install_list_PY_base.apt

```

## Complete Dockerfile Integration Example

Below is a minimal reproducible excerpt demonstrating how the install lists integrate into the build process:

```dockerfile

# docker_core/Dockerfile (excerpt)

FROM ubuntu:22.04 AS build

# Copy utility script and install lists

COPY docker_atom/work/script-utils.sh /opt/utils/
COPY docker_core/work/install_list_PY_base.apt /opt/utils/
COPY docker_core/work/install_list_PY_base.pip /opt/utils/

# Install packages using the helper functions

RUN set -ex \
    && source /opt/utils/script-utils.sh \
    && install_apt /opt/utils/install_list_PY_base.apt \
    && install_pip /opt/utils/install_list_PY_base.pip \
    && list_installed_packages

```

The full Dockerfile includes additional layers for CUDA, R, and Julia support. Refer to the complete file at `docker_core/Dockerfile` in the repository for the comprehensive build sequence.

## Summary

- **Install lists** are plain-text files in `docker_core/work/` using `.apt`, `.pip`, or `.conda` extensions.
- **Comments** start with `%`; the parser uses `cut -d '%' -f 1` to strip them before installation.
- **Helper functions** in [`docker_atom/work/script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/docker_atom/work/script-utils.sh) handle the actual package manager calls.
- **Automatic rebuilds** trigger via GitHub Actions when you commit changes to install-list files.
- **Verification** uses `list_installed_packages()` or manual container testing to confirm successful installation.

## Frequently Asked Questions

### Can I use install lists to remove packages from a LabNow AI image?

No. The `install_apt`, `install_pip`, and `install_conda` functions only support adding packages. To remove software, you must modify the underlying Dockerfile layers or create a custom downstream image that uses `apt-get remove`, `pip uninstall`, or `conda remove` commands after sourcing the base image.

### How does LabNow AI handle version pinning in install lists?

The current implementation passes package names directly to the package managers without explicit version constraints in the list files themselves. For pinned versions, you can specify the version in the list file using standard package manager syntax (e.g., `numpy==1.24.0` for pip or `numpy=1.24.0` for conda), as the helpers pass these strings directly to the install commands.

### What happens if a package in my install list fails to install?

The build fails fast. The helper functions in [`script-utils.sh`](https://github.com/labnow-ai/lab-foundation/blob/main/script-utils.sh) use `set -e` behavior (either explicitly or via the calling Dockerfile's `set -ex`), meaning any non-zero exit code from `apt-get`, `pip`, or `conda` will halt the Docker build process. Check the GitHub Actions logs in [`.github/workflows/build-docker.yml`](https://github.com/labnow-ai/lab-foundation/blob/main/.github/workflows/build-docker.yml) to identify the specific package causing the failure.

### Can I create new install list files with custom names?

Yes, provided you reference them correctly in the Dockerfile. The `install_apt`, `install_pip`, and `install_conda` functions accept any file path as an argument. Create a new file following the `install_list_<scope>.<ext>` convention, then add a corresponding `RUN` instruction in the Dockerfile that points to your new list, similar to how `docker_core/Dockerfile` references the standard lists at lines 42 and 91.