UC Pocket Ubuntu OS: From 303MB to 80MB with Docker Optimization

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

Apr 17, 2025 - 12:12
 0
UC Pocket Ubuntu OS: From 303MB to 80MB with Docker Optimization

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