Dagger is a programmable CI/CD engine that runs your pipelines inside containers1 which makes it easier to test things locally and to setup complex workflows. Not having to write yaml/bash/etc as the workflow gets more complex is a huge advantage for me.

I’m working on a personal project that will allow people to deploy their code by selecting a GitHub repository. Me and a friend have decided to use Dagger to clone the user provided Git repository and build a Docker image and it was extremely easy to get it working.

pub async fn build(repo_url: &str, branch: &str, image: &str) -> Result<String> {
    let client = dagger_sdk::connect()
        .map_err(|err| anyhow!("connecting to dagger: {}", err.to_string()))?;

    let git_ref = client

    let container_registry_secret = client.set_secret("CONTAINER_REGISTRY_SECRET", "TODO");
    let registry_secret_id = container_registry_secret
        .map_err(|err| anyhow!("fetching secret id: {}", err.to_string()))?;

    let directory_id = git_ref
        .map_err(|err| anyhow!("fetching git tree directory id: {}", err.to_string()))?;

    let image_ref = client
        .with_directory("/app", directory_id)
        .with_exec(vec!["npm", "install", "--only=production"])
        .with_entrypoint(vec!["npm", "start"])
        .with_registry_auth("docker.io", "poorlydefinedbehaviour", registry_secret_id)
        .map_err(|err| anyhow!("publishing container image: {}", err.to_string()))?;

    info!("pushed image to container registry: {image_ref}");


The container building part will get more complex as we add more functionality and Dagger will certainly become more appreciated.

Since we are dealing with user provided code, we cannot trust that the provided code will not try to break the system. We are planning to run containers that contain untrusted code inside sandboxes in Kubernetes using kata-containers and a virtual machine monitor like AWS’s Firecracker.