> ## Documentation Index
> Fetch the complete documentation index at: https://aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploying containers

> Build Go containers with Bazel using go_image and rules_oci on a Distroless base, producing reproducible, parallel-layered OCI images for deployment.

Production deployments often require a container image for a service or application, to be run in a Docker daemon, Kubernetes cluster, or other container-based runtime. Container images are merely archive (tar) files, containing some metadata and layers, each of which is another archive file.

Container images are a great use case to be built with Bazel, which has advantages over the `Dockerfile` / `docker build` workflow. It is more reproducible, and much higher performance since individual layers can be built in parallel.

## Build a container with a Go binary

The [Go starter template](https://github.com/aspect-starters/go) includes support for OCI containers.

When you run `aspect gazelle` you should see a `go_image` rule was automatically written in each `BUILD` file next to the `go_binary` target. Of course, it’s also possible to write this target by hand.

By default, `go_image` uses the `base` image from the Distroless project, to minimize dependencies and reduce your exposure to vulnerabilities. See the `oci.pull` call in `MODULE.bazel`.

<Callout icon="book">
  Read more: [https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md](https://github.com/GoogleContainerTools/distroless/blob/main/base/README.md)
</Callout>

```python theme={null}
go_binary(
    name = "hello",
    embed = [":hello_lib"],
    visibility = ["//visibility:public"],
)

go_image(
    name = "image",
    binary = "hello",
)
```

Building this target results in a container image written to `bazel-out`, as a directory following the [OCI image layout specification](https://github.com/opencontainers/image-spec/blob/main/image-layout.md):

```bash theme={null}
% bazel build cmd/hello:image
INFO: Analyzed target //cmd/hello:image (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //cmd/hello:image up-to-date:
  bazel-bin/cmd/hello/image

% ls bazel-bin/cmd/hello/image 
blobs           index.json      oci-layout
```

> Q. Why does it produce a folder rather than a `.tar` file?
> A. This is because container images can be large, and when a Bazel action produces a large output, it will be stored in the remote cache. This causes expensive and slow network transfer, especially when a large number of image cache entries are invalidated by some code changes.

## Running a local container

We’ll load the image data into a locally running daemon, like with Docker or Podman. Make sure you have a container daemon running, and maybe set the `DOCKER_HOST` environment variable so that it can be located by commands like `docker load`.

The `go_image` macro in our `BUILD` file expands to several targets, which you can inspect with `bazel query`. One is `oci_load rule //cmd/hello:image.load` which is executable, so we can load our image by running it.

```bash theme={null}
% bazel run //cmd/hello:image.load
INFO: Analyzed target //cmd/hello:image.load (0 packages loaded, 0 targets configured).
INFO: Found 1 target...
Target //cmd/hello:image.load up-to-date:
  bazel-bin/cmd/hello/image.load.sh
INFO: Running command line: bazel-bin/cmd/hello/image.load.sh
Loaded image: localhost/cmd/hello:latest
```

This printed the image and tag (”latest”) which the daemon has stored. Starting the container then uses the `docker` or `podman` command like you’d use outside of Bazel, for example:

```bash theme={null}
% podman run --rm localhost/cmd/hello -p 8080:8080
```

Navigate again to [http://localhost:8080](http://localhost:8080) in your browser to access the application.

## Pushing to a remote registry

To run the application in a remote environment like on a Cloud service, we’ll need to run a `push` command. The `oci_push` rule creates an executable target that can be used with `bazel run`. See [https://github.com/bazel-contrib/rules\_oci/blob/main/docs/push.md](https://github.com/bazel-contrib/rules_oci/blob/main/docs/push.md)

However we would need credentials for a registry to perform a push, so this lesson doesn’t include that.

## Continuous Delivery

Ideally our `go_image` should be pushed to the remote registry any time we’ve landed code changes that affect the binary. This way the developer workflow is simply to merge changes, then run a deployment command that promotes a binary built during CI. This is better than running a `push` command locally, because we are guaranteed that the container image is exactly the one that passed all the tests.

See [Selective delivery](/docs/aspect-workflows/platform/features/selective-delivery) for our recommended approach.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.