Introduction

As developers and DevOps engineers, we constantly seek ways to optimize our environments—reducing image sizes, improving security, and maintaining performance. In this article, I'll walk you through how I transformed a standard 303MB Ubuntu Docker image into a lean 80MB distroless-based container while retaining full functionality (SSH, Apache, and persistent storage).

We'll cover:

Multi-stage builds to eliminate unnecessary dependencies

Distroless optimization for security and size reduction

Persistent storage to retain data across container restarts

DevSecOps benefits (smaller attack surface, faster CI/CD)

How non-Linux users can replace VMs with this pocket Ubuntu


The Problem: Bloated Ubuntu Images

Initially, my Dockerfile used a standard Ubuntu base with SSH and Apache:

Original Dockerfile (303MB)

FROM ubuntu:latest

# Set environment variables
ENV TZ=Africa/Lagos
ENV LANG=en_US.UTF-8

# Install packages
RUN apt-get update && \
    apt-get install -y \
    tzdata \
    locales \
    openssh-server \
    apache2 \
    && rm -rf /var/lib/apt/lists/*

# Configure timezone & locale
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone && \
    locale-gen en_US.UTF-8

# Set up SSH
RUN mkdir /var/run/sshd && \
    echo 'root:password' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Expose ports
EXPOSE 22 80

# Persistent data volume
RUN mkdir /data

# Keep container running
RUN echo '#!/bin/bash\n\
service ssh start\n\
service apache2 start\n\
tail -f /dev/null' > /entrypoint.sh && \
    chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]

Issues with this approach:

303MB is too large for a minimal Ubuntu environment

Unnecessary packages increase security risks

No multi-stage optimization


The Solution: Distroless + Multi-Stage Builds

By using multi-stage builds and distroless base images, we reduce bloat while keeping essential functionality.

Optimized Dockerfile (80MB)

# Stage 1: Builder (Full Ubuntu)
FROM ubuntu:22.04 AS builder

# Install only essentials
RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    openssh-server \
    apache2 \
    && rm -rf /var/lib/apt/lists/*

# Configure SSH
RUN mkdir /var/run/sshd && \
    echo 'root:password' | chpasswd && \
    sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# Stage 2: Distroless Runtime
FROM gcr.io/distroless/base-debian11

# Copy only necessary binaries
COPY --from=builder /usr/sbin/sshd /usr/sbin/
COPY --from=builder /usr/sbin/apache2 /usr/sbin/
COPY --from=builder /bin/bash /bin/

# Copy essential libraries
COPY --from=builder /lib/x86_64-linux-gnu/ /lib/x86_64-linux-gnu/
COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/

# Set up persistent storage
RUN mkdir /data && chmod 777 /data
VOLUME /data

# Entrypoint script
RUN echo '#!/bin/bash\n\
/usr/sbin/sshd -D &\n\
/usr/sbin/apache2 -DFOREGROUND &\n\
tail -f /dev/null' > /entrypoint.sh && \
    chmod +x /entrypoint.sh

EXPOSE 22 80
ENTRYPOINT ["/entrypoint.sh"]

Key Optimizations

Multi-stage build separates build and runtime dependencies

Distroless base removes unnecessary packages (no shell, no bloat)

Only essential binaries copied (SSH, Apache, Bash)

Persistent /data volume for file storage


How to Use UC Pocket Ubuntu

1. First, Run the Container in Detached Mode

docker run -d \
  -p 2222:22 -p 8080:80 \
  -v ~/ubuntu-data:/data \
  --name my-ubuntu \
  ucheenyi/uc-pocket-ubuntu

2. Then, Execute Commands Inside the Running Container

# Get an interactive bash shell
docker exec -it my-ubuntu bash

# Or run single commands
docker exec my-ubuntu ls /data
docker exec my-ubuntu apt-get update

3. Access Services

  • SSH: ssh root@localhost -p 2222 (password: password)
  • Apache: Open http://localhost:8080

4. Stop and Remove When Done

docker stop my-ubuntu
docker rm my-ubuntu
# Your data persists in ~/ubuntu-data on host

Why This Matters for DevSecOps & DevOps

🔹 Smaller Attack Surface (No unnecessary packages = fewer CVEs)

🔹 Faster CI/CD Pipelines (Smaller images = quicker deployments)

🔹 Cost-Efficient (Replaces heavy VMs with lightweight containers)

🔹 Cross-Platform (Windows/Mac users get full Ubuntu CLI without VMs)


Final Thoughts

By applying multi-stage builds and distroless optimization, we reduced the image size by 73% while keeping all functionality.

My Dockerhub link for the image:

https://hub.docker.com/r/ucheenyi/uc-pocket-ubuntu

This approach is perfect for:

Developers needing a lightweight Ubuntu environment

DevOps teams optimizing CI/CD pipelines

Security teams minimizing attack surfaces