OCI/Docker image tar layer squashing tool (pure Python stdlib)
Project description
OCI-Squash - Standalone Docker/OCI Image Layer Squashing Tool
A lightweight, dependency-free tool to squash Docker/OCI image layers from saved tar archives. It works fully offline (no Docker daemon required) and produces Docker-loadable tar output.
Features
- Zero Dependencies: Pure Python standard library at runtime
- Docker & OCI Support: Auto-detects both formats; handles nested OCI indexes
- Standalone: Works on image tar files without Docker daemon
- Docker-loadable Output: Always emits Docker-style layers for reliable
docker load - Metadata Preservation: Maintains config/history and computes correct
diff_ids - Whiteout Handling: Properly reinjects marker files; supports opaque dirs
- Tagging: Set repository tag for the squashed image
- Size Reporting: Prints original vs squashed tar sizes and percentage change
Installation
pip install oci-squash
docker save -o source.tar myrepo/myimage:latest
oci-squash -f 3 -t myrepo/myimage:squashed -o squashed.tar source.tar
docker load -i squashed.tar
End-to-end Example
This example squashes the last 8 layers of lyonv/ubuntu-dev:latest, saves to squashed.tar, loads it back, and shows the reduced history and tar size.
- Inspect original image history:
docker history lyonv/ubuntu-dev:latest
IMAGE CREATED CREATED BY SIZE COMMENT
b23aed85a512 10 hours ago RUN /bin/sh -c rm -f /bigfile # buildkit 0B buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c dd if=/dev/zero of=/bigfile b… 105MB buildkit.dockerfile.v0
<missing> 10 hours ago WORKDIR /app && USER appuser && echo Ver… 0B buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c apt-get clean && rm -rf /… 4.1kB buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c mkdir -p /app/{conf,logs} /va… 0B buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c apt-get install -y --no-insta… 2.83MB buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c apt-get install -y --no-insta… 78.1MB buildkit.dockerfile.v0
<missing> 10 hours ago RUN /bin/sh -c ln -snf /usr/share/zoneinfo/A… 58.4MB buildkit.dockerfile.v0
<missing> 11 months ago sh -c sleep 36000 4.1kB build img
<missing> 15 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 15 months ago /bin/sh -c #(nop) ADD file:e7cff353f027ecf0a… 79.5MB
<missing> 15 months ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 15 months ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 15 months ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 15 months ago /bin/sh -c #(nop) ARG RELEASE 0B
- Save, squash, and load:
docker save -o source.tar lyonv/ubuntu-dev:latest
oci-squash -f 8 -t lyonv/ubuntu-dev:squashed -m squashed -o squashed.tar source.tar
docker load -i squashed.tar
Sample run output:
2025-08-30 17:18:56,654 cli.py:106 INFO Extracting tar: source.tar
2025-08-30 17:18:56,859 cli.py:109 INFO Detected format: oci
2025-08-30 17:18:56,859 cli.py:116 INFO Attempting to squash last 8 layers
2025-08-30 17:18:57,965 cli.py:162 INFO Exporting to: squashed.tar
2025-08-30 17:18:58,393 cli.py:164 INFO Done. New image id: sha256:1ae6a77f5b834850e9a9b2c4e3a6f8f715efb8c46af92635a49640c53e3db347
2025-08-30 17:18:58,393 cli.py:171 INFO Original tar size: 299.76 MB
2025-08-30 17:18:58,393 cli.py:172 INFO Squashed tar size: 143.15 MB
2025-08-30 17:18:58,393 cli.py:175 INFO Tar size decreased by 52.24 %
2025-08-30 17:18:58,484 cli.py:186 INFO Squashed image Done.
Loaded image: lyonv/ubuntu-dev:squashed
- Inspect the squashed image history:
docker history lyonv/ubuntu-dev:squashed
IMAGE CREATED CREATED BY SIZE COMMENT
927444bd87b4 31 seconds ago 79.9MB squashed
<missing> 11 months ago sh -c sleep 36000 4.1kB build img
<missing> 15 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 15 months ago /bin/sh -c #(nop) ADD file:e7cff353f027ecf0a… 79.5MB
<missing> 15 months ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 15 months ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B
<missing> 15 months ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B
<missing> 15 months ago /bin/sh -c #(nop) ARG RELEASE 0B
In this run, the tar reduced from 299.76 MB to 143.15 MB, a 52.24% decrease.
You can run it via Python (local dev tree):
PYTHONPATH=src python -m oci_squash.cli -h
or via the built binary:
./dist/oci-squash -h
Usage
usage: oci-squash [-h] [-f FROM_LAYER] [-t TAG] [-c [CLEANUP]] [-m MESSAGE] [--tmp-dir TMP_DIR] [-o OUTPUT_PATH] [-v] image
OCI/Docker image tar layer squashing tool
positional arguments:
image Path to image tar file
options:
-h, --help show this help message and exit
-f FROM_LAYER, --from-layer FROM_LAYER
Number of layers to squash or layer id
-t TAG, --tag TAG Tag for squashed image, e.g. repo/name:tag
-c [CLEANUP], --cleanup [CLEANUP]
Cleanup the temporary directory (true/false). Default: true
-m MESSAGE, --message MESSAGE
Commit message for the new image
--tmp-dir TMP_DIR Work directory to use (kept if provided)
-o OUTPUT_PATH, --output-path OUTPUT_PATH
Output tar path for the squashed image
-v, --verbose Verbose output
Notes:
--from-layeraccepts either a number of layers from the top (e.g.,-f 3) or an existing layer id/digest found in the image history/manifest.--cleanupis a boolean with defaulttrue. Use--cleanup falseto keep the work directory for debugging.--output-pathsets the output tar file. If omitted, a name is generated based on the new image id.
Quick Start
- Save an image to a tar archive:
docker save -o source.tar myrepo/myimage:latest
- Squash the last N layers and write a new tar:
oci-squash -f 8 -t myrepo/myimage:squashed -m "squashed" -o squashed.tar source.tar
- Load the resulting image into Docker:
docker load -i squashed.tar
- Inspect the result:
docker history myrepo/myimage:squashed
Example Output
Below is a real run showing tar size comparison and a successful load:
2025-08-30 10:22:19,894 cli.py:106 INFO Extracting tar: source.tar
2025-08-30 10:22:20,101 cli.py:109 INFO Detected format: oci
2025-08-30 10:22:20,102 cli.py:116 INFO Attempting to squash last 8 layers
2025-08-30 10:22:21,214 cli.py:162 INFO Exporting to: squashed.tar
2025-08-30 10:22:21,646 cli.py:164 INFO Done. New image id: sha256:0defd0fe8f1ea371...
2025-08-30 10:22:21,646 cli.py:171 INFO Original tar size: 299.76 MB
2025-08-30 10:22:21,646 cli.py:172 INFO Squashed tar size: 143.15 MB
2025-08-30 10:22:21,646 cli.py:175 INFO Tar size decreased by 52.24 %
2025-08-30 10:22:21,737 cli.py:186 INFO Squashed image Done.
Loaded image: myrepo/myimage:squashed
How It Works (Brief)
- Extracts input tar into a work directory and detects format (Docker/OCI)
- Reads manifest and config; builds complete layer sequence (including virtual empty layers)
- Squashes selected layers by reassembling files, respecting whiteouts/opaque directories
- Always writes Docker-style output (
<digest>/layer.tarand optionalsquashed/layer.tar) - Recomputes
diff_idsand config/rootfs/history; writes a newmanifest.jsonandrepositories - Packs the new directory back into a tar that
docker loadcan consume
Tips
- Use
-v/--verboseto print detailed processing steps - Keep
--tmp-dirto a local fast disk for better performance - If you want to inspect the work directory, pass
--cleanup false
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oci_squash-0.1.0.tar.gz.
File metadata
- Download URL: oci_squash-0.1.0.tar.gz
- Upload date:
- Size: 12.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc96d7293cf82f7413a2c798df302dae471b6c5e0e312ec77a5e6c7271a9399a
|
|
| MD5 |
ccb347a13249304a097f1f5b012ac130
|
|
| BLAKE2b-256 |
271052bd4f22af0cd1c1a915e619665e0dcef66ed317463256928f3377269c65
|
File details
Details for the file oci_squash-0.1.0-py3-none-any.whl.
File metadata
- Download URL: oci_squash-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ece0d898370c175f36bd6f0eb4e66ddf1a97a08e1a3e87d72346b0aca192d07b
|
|
| MD5 |
5d52491b0401dca9aa3cddf6e483db1c
|
|
| BLAKE2b-256 |
d7884c2431a33d617bff405f162c66d56adff1e0b22553602e0ca10482241c6a
|