Load Container Images into a Local Docker Engine
Introduction
There are two possible approaches to loading container images built with Dagger into a local Docker engine. This tutorial describes them both in detail, although only the first one is recommended.
Requirements
This tutorial assumes that:
- You have a Go, Python or Node.js development environment. If not, install Go, Python or Node.js.
- You have a Dagger SDK installed for one of the above languages. If not, follow the installation instructions for the Dagger Go, Python or Node.js SDK.
- You have the Dagger CLI installed in your development environment. If not, install the Dagger CLI.
- You have Docker installed and running on the host system. If not, install Docker.
Approach 1: Use an exported tarball
This approach involves and exporting the container image to the host filesystem as a TAR file with Dagger, and then loading it into Docker. Here's an example:
- Go
- Node.js
- Python
package main
import (
"context"
"fmt"
"os"
"dagger.io/dagger"
)
func main() {
// initialize Dagger client
ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()
// use NGINX container
// add new webserver index page
c := client.Container(dagger.ContainerOpts{Platform: "linux/amd64"}).
From("nginx:1.23-alpine").
WithNewFile("/usr/share/nginx/html/index.html", dagger.ContainerWithNewFileOpts{
Contents: "Hello from Dagger!",
Permissions: 0o400,
})
// export to host filesystem
val, err := c.Export(ctx, "/tmp/my-nginx.tar")
if err != nil {
panic(err)
}
// print result
fmt.Println("Exported image: ", val)
}
This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
Connect()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
Container().From()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalPlatform
argument to theContainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'sWithNewFile()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
Export()
method to write the final container image to the host filesystem as a TAR file at/tmp/my-nginx.tar
.
import { connect } from "@dagger.io/dagger"
// initialize Dagger client
connect(
async (client) => {
// use NGINX container
// add new webserver index page
const ctr = client
.container({ platform: "linux/amd64" })
.from("nginx:1.23-alpine")
.withNewFile("/usr/share/nginx/html/index.html", {
contents: "Hello from Dagger!",
permissions: 0o400,
})
// export to host filesystem
const result = await ctr.export("/tmp/my-nginx.tar")
// print result
console.log(`Exported image: ${result}`)
},
{ LogOutput: process.stderr },
)
This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
connect()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
container().from()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalplatform
argument to thecontainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'swithNewFile()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
export()
method to write the final container image to the host filesystem as a TAR file at/tmp/my-nginx.tar
.
import sys
import anyio
import dagger
async def main():
# initialize Dagger client
async with dagger.Connection(dagger.Config(log_output=sys.stderr)) as client:
# use NGINX container
# add new webserver index page
ctr = (
client.container(platform=dagger.Platform("linux/amd64"))
.from_("nginx:1.23-alpine")
.with_new_file(
"/usr/share/nginx/html/index.html",
contents="Hello from Dagger!",
permissions=0o400,
)
)
# export to host filesystem
val = await ctr.export("/tmp/my-nginx.tar")
print(f"Exported image: {val}")
anyio.run(main)
This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
dagger.Connection()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
container().from_()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalplatform
argument to thecontainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'swith_new_file()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
export()
method to write the final container image to the host filesystem as a TAR file at/tmp/my-nginx.tar
.
Once exported, the image can be imported into Docker with docker load
:
docker load -i /tmp/my-nginx.tar
...
Loaded image ID: sha256:2e340...
Once imported, the image can be used like any other Docker image. For example, to run the sample image above, use the following docker run
command:
docker run --rm --net=host 2e340...
Approach 2: Use a local registry server
This approach is significantly more complex than the previous one and is therefore not recommended. It is included only for documentation completeness.
The commands in this section deploy a local registry server without authentication or TLS. This is highly insecure and should only be used for local development, debugging and testing. Refer to the Docker documentation for details on how to deploy a more secure and production-ready registry.
This approach involves publishing the container image to a local registry, and then pulling from it as usual with Docker. Follow the steps below:
-
Deploy a local container registry with Docker. This local registry must be configured to run in the same network as the Dagger Engine container, and must use a host volume for the registry data.
tipAt this point, the local registry must run in the same network as the Dagger Engine container so that Dagger is able to communicate with it using the
localhost
or127.0.0.1
network address.DAGGER_CONTAINER_NETWORK_NAME=`docker ps --filter "name=^dagger-engine-*" --format '{{.Names}}'`
docker run -d --rm --name registry --network container:$DAGGER_CONTAINER_NETWORK_NAME -v /opt/docker-registry/data:/var/lib/registry registry:2 -
Create a Dagger pipeline to build and push a container image to the local registry:
- Go
- Node.js
- Python
package main
import (
"context"
"fmt"
"os"
"dagger.io/dagger"
)
func main() {
// initialize Dagger client
ctx := context.Background()
client, err := dagger.Connect(ctx, dagger.WithLogOutput(os.Stderr))
if err != nil {
panic(err)
}
defer client.Close()
// use NGINX container
// add new webserver index page
c := client.Container(dagger.ContainerOpts{Platform: "linux/amd64"}).
From("nginx:1.23-alpine").
WithNewFile("/usr/share/nginx/html/index.html", dagger.ContainerWithNewFileOpts{
Contents: "Hello from Dagger!",
Permissions: 0o400,
})
// publish to local registry
addr, err := c.Publish(ctx, "127.0.0.1:5000/my-nginx:1.0")
if err != nil {
panic(err)
}
// print result
fmt.Println("Published at:", addr)
}This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
Connect()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
Container().From()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalPlatform
argument to theContainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'sWithNewFile()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
Publish()
method to publish the final container image to the local registry.
import { connect } from "@dagger.io/dagger"
// initialize Dagger client
connect(
async (client) => {
// use NGINX container
// add new webserver index page
const ctr = client
.container({ platform: "linux/amd64" })
.from("nginx:1.23-alpine")
.withNewFile("/usr/share/nginx/html/index.html", {
contents: "Hello from Dagger!",
permissions: 0o400,
})
// publish to local registry
const result = await ctr.publish("127.0.0.1:5000/my-nginx:1.0")
// print result
console.log(`Published at: ${result}`)
},
{ LogOutput: process.stderr },
)This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
connect()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
container().from()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalplatform
argument to thecontainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'swithNewFile()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
publish()
method to publish the final container image to the local registry.
import sys
import anyio
import dagger
async def main():
# initialize Dagger client
async with dagger.Connection(dagger.Config(log_output=sys.stderr)) as client:
# use NGINX container
# add new webserver index page
ctr = (
client.container(platform=dagger.Platform("linux/amd64"))
.from_("nginx:1.23-alpine")
.with_new_file(
"/usr/share/nginx/html/index.html",
contents="Hello from Dagger!",
permissions=0o400,
)
)
# export to host filesystem
val = await ctr.publish("127.0.0.1:5000/my-nginx:1.0")
print(f"Published at: {val}")
anyio.run(main)This code listing performs the following operations:
- It imports the Dagger client library.
- It creates a Dagger client with
dagger.Connection()
. This client provides an interface for executing commands against the Dagger engine. - It uses the client's
container().from_()
method to initialize a new container from a base image (nginx:1.23-alpine
). The additionalplatform
argument to thecontainer()
method instructs Dagger to build for a specific architecture (linux/amd64
). This method returns aContainer
representing an OCI-compatible container image. - It uses the previous
Container
object'swith_new_file()
method to create a new file at the NGINX web server root and return the result as a newContainer
. - It uses the
publish()
method to publish the final container image to the local registry.
-
Run the Dagger pipeline:
- Go
- Node.js
- Python
dagger run go run ci/main.go
dagger run node ci/index.mjs
dagger run python ci/main.py
The
dagger run
command executes the script in a Dagger session and displays live progress. At the end of the process, the built container is pushed to the local registry and a message similar to the one below appears in the console output:Published at: 127.0.0.1:5000/my-nginx:1.0@sha256:c59a...
-
Stop the local registry, then restart it after detaching it from the Dagger Engine container network and publishing the registry server port so that it can be used from the Docker host.
tipAt this point, the local registry must run in the same network as the host so that you can use it via the
localhost
or127.0.0.1
network address.docker stop registry
docker run -d --rm --name registry -p 5000:5000 -v /opt/docker-registry/data:/var/lib/registry registry:2
The image can now be pulled from the registry and used like any other Docker image. For example, to run the sample image above, use the following docker run
command:
docker run --net=host 127.0.0.1:5000/my-nginx:1.0@sha256:c59a...
Conclusion
This tutorial walked you through two approaches to building container images with Dagger purely for local use: exporting the image as a tarball and loading it into Docker, or pushing the image to a local container registry.
Use the API Key Concepts page and the Go, Node.js and Python SDK References to learn more about Dagger.