I've been setting up a little homelab and wanted to be able to convert docker images to LXC containers. Given the LXC container is just a tar.gz of the rootfs, and docker images are just a tar.gz of a bunch of filesystem layers, I threw together a quick script that converts them without needing to use docker to run/create the container.
A quick note, the docker engine handles running the ENTRYPOINT/CMD commands. This won't do that so you _will_ need to update the LXC container, once running, to do that. I might add that in but it strongly depends on the linux distribution used (e.g. systemd, upstart, etc).
The script is here but I'll include it below as well.
A quick note, the docker engine handles running the ENTRYPOINT/CMD commands. This won't do that so you _will_ need to update the LXC container, once running, to do that. I might add that in but it strongly depends on the linux distribution used (e.g. systemd, upstart, etc).
The script is here but I'll include it below as well.
Bash:
#!/bin/bash
# Check if image name is provided
if [ -z "$1" ]; then
echo "Usage: $0 <image-name>[:<tag>]"
echo "Example: $0 ubuntu:latest"
exit 1
fi
# Assign input image name
IMAGE="$1"
# Generate output filename based on image name, replacing '/' and ':' with '_'
OUTPUT_NAME=$(echo "${IMAGE}" | sed 's/[:\/]/_/g').tar.gz
# Temporary directories
TEMP_DIR=$(mktemp -d)
EXTRACT_DIR="${TEMP_DIR}/extract"
ROOTFS_DIR="${TEMP_DIR}/rootfs"
# Check if Docker is installed and running
if ! command -v docker &> /dev/null; then
echo "Error: Docker is not installed."
exit 1
fi
if ! docker info &> /dev/null; then
echo "Error: Docker daemon is not running."
exit 1
fi
# Check if jq is installed (for parsing JSON)
if ! command -v jq &> /dev/null; then
echo "Error: jq is required for parsing JSON. Install it with 'apt install jq' or similar."
exit 1
fi
# Check if the image exists locally, pull if it doesn't
if ! docker image inspect "${IMAGE}" &> /dev/null; then
echo "Pulling image ${IMAGE}..."
docker pull "${IMAGE}" || {
echo "Error: Failed to pull image ${IMAGE}."
exit 1
}
fi
echo "Saving image ${IMAGE} to tar..."
# Save the image to a tar file
IMAGE_TAR="${TEMP_DIR}/image.tar"
docker save -o "${IMAGE_TAR}" "${IMAGE}" || {
echo "Error: Failed to save image."
exit 1
}
echo "Extracting image tar..."
# Create extract directory and extract the tar
mkdir -p "${EXTRACT_DIR}"
tar -xf "${IMAGE_TAR}" -C "${EXTRACT_DIR}" || {
echo "Error: Failed to extract image tar."
rm -rf "${TEMP_DIR}"
exit 1
}
echo "Parsing manifest.json..."
# Parse the manifest.json to get the layers (assuming single image manifest)
MANIFEST="${EXTRACT_DIR}/manifest.json"
if [ ! -f "${MANIFEST}" ]; then
echo "Error: manifest.json not found."
rm -rf "${TEMP_DIR}"
exit 1
fi
LAYERS=$(jq -r '.[0].Layers[]' "${MANIFEST}")
# Create rootfs directory
mkdir -p "${ROOTFS_DIR}"
echo "Extracting and merging layers..."
# For each layer, extract and handle whiteouts
for LAYER in ${LAYERS}; do
LAYER_TAR="${EXTRACT_DIR}/${LAYER}"
if [ ! -f "${LAYER_TAR}" ]; then
echo "Error: Layer tar ${LAYER} not found."
rm -rf "${TEMP_DIR}"
exit 1
fi
# Extract the layer tar to rootfs
tar -xf "${LAYER_TAR}" -C "${ROOTFS_DIR}"
# Handle whiteouts: find all .wh.* files
find "${ROOTFS_DIR}" -type f -name '.wh.*' | while read -r WHITEOUT; do
# Get the directory and the name to delete
DIR=$(dirname "${WHITEOUT}")
NAME=$(basename "${WHITEOUT}" | sed 's/^\.wh\.//')
if [ "${NAME}" = ".wh..opq" ]; then
# Opaque directory: remove all contents except whiteouts (but for flatten, we can skip or handle as delete dir)
# For simplicity, treat as deleting the directory contents from lower layers, but since we extract in order, just remove the marker
rm -f "${WHITEOUT}"
else
# Regular whiteout: remove the actual file/dir if exists
TARGET="${DIR}/${NAME}"
rm -rf "${TARGET}"
# Remove the whiteout marker
rm -f "${WHITEOUT}"
fi
done
done
echo "Compressing rootfs to ${OUTPUT_NAME}..."
# Create the tar.gz from rootfs
tar -czf "${OUTPUT_NAME}" -C "${ROOTFS_DIR}" . || {
echo "Error: Failed to create ${OUTPUT_NAME}."
rm -rf "${TEMP_DIR}"
exit 1
}
# Clean up
echo "Cleaning up temporary files..."
rm -rf "${TEMP_DIR}"
echo "Success: Root filesystem built and exported to ${OUTPUT_NAME}"
Last edited: