Skip to content
alienplatformPublic

About

Build and push Docker images from Rust without Docker installed

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

14 Commits

Folders and files

Repository files navigation

dockdash

Build and push Docker images from Rust πŸ¦€ β€” without Docker installed.

crates.io docs.rs CI License


Dockdash is a Rust library for building and pushing Docker (OCI) images β€” no Docker daemon, no docker CLI, no privileged containers. Just a normal Rust crate that produces real container images and ships them to any registry.

It's fast enough to run inside a serverless function: build an image in a Lambda, push it to ECR, and exit before your cold start budget runs out.

Why Dockdash?

  • πŸ”₯ Blazingly fast β€” native Rust, zstd layer compression, and content-addressable blob caching. Builds images in milliseconds, not seconds.
  • ☁️ Runs anywhere β€” serverless functions, sandboxed CI, edge runtimes, CLIs. If Rust runs there, Dockdash works there.
  • 🐳 No Docker required β€” no daemon, no socket, no root. Just a library call.
  • 🧱 Simple API β€” a builder pattern for layers and images. No Dockerfiles, no shelling out.
  • 🌍 Multi-arch β€” build for amd64, arm64, and any other platform you need.
  • πŸ“¦ Push to any OCI registry β€” Docker Hub, ECR, GCR, ACR, GHCR, or your own. Anonymous, basic auth, and token auth supported.
  • ⚑ Incremental builds β€” local content-addressable cache means unchanged layers are reused instantly.

Layer compression

New layers use zstd by default. For runtimes that require gzip, such as AWS Lambda, select gzip when building each application layer:

use dockdash::{Layer, LayerCompression};

# async fn example() -> dockdash::Result<()> {
let layer = Layer::builder()?
    .compression(LayerCompression::Gzip)
    .file("./app", "/app", Some(0o755))?
    .build()
    .await?;
# Ok(())
# }

The image manifest preserves each layer's compression format. Gzip and zstd layers have separate cache entries. Existing base-image layers retain their original compression, so choose a compatible base image as well.

Quick Start

Add to your Cargo.toml:

[dependencies]
dockdash = "0.2"
tokio = { version = "1", features = ["full"] }

Build and push an image:

use dockdash::{Arch, Image, Layer, PushOptions, Result};
use std::path::PathBuf;

#[tokio::main]
async fn main() -> Result<()> {
    // Create a layer from a local binary
    let layer = Layer::builder()?
        .file("./target/release/my-app", "./my-app", Some(0o755))?
        .build()
        .await?;

    // Build the OCI image
    let (image, _) = Image::builder()
        .from("ubuntu:latest")
        .platform("linux", &Arch::ARM64)
        .layer(layer)
        .entrypoint(vec!["/my-app".to_string()])
        .output_to(PathBuf::from("image.oci.tar"))
        .build()
        .await?;

    // Push to a registry
    image.push("my-registry.com/my-app:latest", &PushOptions::default()).await?;

    Ok(())
}

Features

Layer Builder

Create layers from files or raw data:

// From a file on disk
let layer = Layer::builder()?
    .file("./my-binary", "./app/my-binary", Some(0o755))?
    .build()
    .await?;

// From raw bytes
let layer = Layer::builder()?
    .data("./app/config.toml", config_bytes, None)?
    .build()
    .await?;

// Multiple files in one layer
let layer = Layer::builder()?
    .file("./binary", "./app/binary", Some(0o755))?
    .file("./config.toml", "./app/config.toml", None)?
    .data("./app/version.txt", b"1.0.0", None)?
    .build()
    .await?;

Image Builder

Compose images from a base and custom layers:

let (image, diagnostics) = Image::builder()
    .from("alpine:3.19")
    .platform("linux", &Arch::Amd64)
    .layer(app_layer)
    .layer(config_layer)
    .entrypoint(vec!["/app/server".to_string()])
    .working_dir("/app")
    .build()
    .await?;

Blob Caching

Speed up repeated builds with content-addressable caching:

use dockdash::BlobCache;

// Default location: ~/.dockdash/cache/blobs
let cache = BlobCache::new()?;

// Or use a custom cache directory (path is used as-is)
let cache = BlobCache::with_path("/my/custom/cache".into())?;

let layer = Layer::builder()?
    .blob_cache(cache.clone())
    .file("./my-binary", "./app/my-binary", Some(0o755))?
    .build()
    .await?;

let (image, _) = Image::builder()
    .from("alpine:latest")
    .blob_cache(cache)
    .layer(layer)
    .build()
    .await?;

Registry Authentication

use dockdash::{RegistryAuth, PushOptions, ClientProtocol};

// Anonymous (e.g., ttl.sh)
let opts = PushOptions::default();

// Basic auth
let opts = PushOptions {
    auth: RegistryAuth::Basic("user".into(), "pass".into()),
    ..Default::default()
};

// HTTP (for local registries)
let opts = PushOptions {
    protocol: ClientProtocol::Http,
    ..Default::default()
};

Use Cases

  • Serverless functions β€” build and push images from Lambda, Cloud Run, Vercel, or Cloudflare Workers, in milliseconds.
  • CI/CD pipelines β€” ship container images without Docker-in-Docker, root, or privileged runners.
  • Deployment platforms β€” bake user code into images on the fly as part of your build/deploy pipeline.
  • CLI tools β€” embed container image building directly into your Rust CLI, no external dependencies.
  • Edge & embedded β€” build images on resource-constrained devices where Docker simply isn't an option.

Testing

# Unit tests
cargo test

# Integration tests (requires Docker for Bollard-based tests)
cargo test --features test-utils

License

Licensed under the Apache License, Version 2.0.

About

Build and push Docker images from Rust without Docker installed

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages