# Tutorial Take a guided tour of `container` by building, running, and publishing a simple web server image. ## Try out the `container` CLI Start the application, and try out some basic commands to familiarize yourself with the command line interface (CLI) tool. ### Start the container service Start the services that `container` uses: ```bash container system start ``` If you have not installed a Linux kernel yet, the command will prompt you to install one: ```shellsession % container system start Verifying apiserver is running... Done Missing required runtime dependencies: 1. Initial Filesystem 2. Kernel Would like to install them now? [Y/n]: Y Installing default kernel from [https://github.com/kata-containers/kata-containers/releases/download/3.17.0/kata-static-3.17.0-arm64.tar.xz]... Installing initial filesystem from [ghcr.io/apple/containerization/vminit:0.1.34]... % ``` Then, verify that the application is working by running a command to list all containers: ```bash container list --all ``` If you haven't created any containers yet, the command outputs an empty list: ```shellsession % container list --all ID IMAGE OS ARCH STATE ADDR % ``` ### Get CLI help You can get help for any `container` CLI command by appending the `--help` option: ```shellsession % container --help OVERVIEW: A container platform for macOS USAGE: container [--debug] OPTIONS: --debug Enable debug output [environment: CONTAINER_DEBUG] --version Show the version. -h, --help Show help information. CONTAINER SUBCOMMANDS: create Create a new container delete, rm Delete one or more containers exec Run a new command in a running container inspect Display information about one or more containers kill Kill one or more running containers list, ls List containers logs Fetch container stdio or boot logs run Run a container start Start a container stop Stop one or more running containers IMAGE SUBCOMMANDS: build Build an image from a Dockerfile images, image, i Manage images registry, r Manage registry configurations SYSTEM SUBCOMMANDS: builder Manage an image builder instance system, s Manage system components % ``` ### Abbreviations You can save keystrokes by abbreviating commands and options. For example, abbreviate the `container list` command to `container ls`, and the `--all` option to `-a`: ```shellsession % container ls -a ID IMAGE OS ARCH STATE ADDR % ``` Use the `--help` flag to see which abbreviations exist. ### Set up a local DNS domain (optional) `container` includes an embedded DNS service that simplifies access to your containerized applications. If you want to configure a local DNS domain named `test` for this tutorial, run: ```bash sudo container system dns create test ``` Enter your administrator password when prompted. The command requires administrator privileges to create a file containing the domain configuration under the `/etc/resolver` directory, and to tell the macOS DNS resolver to reload its configuration files. ## Build an image Set up a `Dockerfile` for a basic Python web server, and use it to build a container image named `web-test`. ### Set up a simple project Start a terminal, create a directory named `web-test` for the files needed to create the container image: ```bash mkdir web-test cd web-test ``` Download an image file for your web server can use: ```bash curl -L -o logo.jpg https://github.com/apple/container/tree/main/docs/assets/logo.jpg ``` In the `web-test` directory, create a file named `Dockerfile` with this content: ```docker FROM docker.io/python:slim WORKDIR /content COPY logo.jpg ./ RUN echo 'Hello

Hello, world!

' > index.html CMD ["python3", "-m", "http.server", "80", "--bind", "0.0.0.0"] ``` The `FROM` line instructs the `container` builder to start with a base image containing the latest production version of Python 3. The `WORKDIR` line creates a directory `/content` in the image, and makes it the current directory. The `COPY` command copies the image file `logo.jpg` from your build context to the image. See the following section for a description of the build context. The `RUN` line creates a simple HTML landing page named `/content/index.html`. The `CMD` line configures the container to run a simple web server in Python on port 80. Since the working directory is `/content`, the web server runs in that directory and delivers the content of the file `/content/index.html` when a user requests the index page URL. The server binds to the wildcard address `0.0.0.0` to allow connections from the host and other containers. To ensure security, the virtual network used by the containers is not accessible by external systems. ### Build the web server image Run the `container build` command to create an image with the name `web-test` from your `Dockerfile`: ```bash container build --tag web-test --file Dockerfile . ``` The last argument `.` tells the builder to use the current directory (`web-test`) as the root of the build context. You can copy files within the build context into your image using the `COPY` command in your Dockerfile. After the build completes, list the images. You should see both the base image and the image that you built in the results: ```shellsession % container images list NAME TAG DIGEST docker.io/library/python slim 56a11364ffe0fee3bd60af6d... web-test latest bf91dc9d42f0110d3aac41dd... % ``` ## Run containers Using your container image, run a web server and try out different ways of interacting with it. ### Start the webserver Use `container run` to start a container named `my-web-server` that runs your webserver: ```bash container run --name my-web-server --dns-domain test --detach --rm web-test ``` The `--detach` flag runs the container in the background, so that you can continue running commands in the same terminal. The `--rm` flag causes the container to be removed automatically after it stops. When you list containers now, `my-web-server` is present, along with the container that `container` started to build your image. Note that its IP address, shown in the `ADDR` column, is `192.168.64.3`: ```shellsession % container ls ID IMAGE OS ARCH STATE ADDR buildkit ghcr.io/apple/container-builder-shim/builder:2.1.1 linux arm64 running 192.168.64.2 my-web-server web-test:latest linux arm64 running 192.168.64.3 % ``` Open the website, using the container's IP address in the URL: ```bash open http://192.168.64.3 ``` If you configured the local domain `test` earlier in the tutorial, you can also open the page the full hostname for the container: ```bash open http://my-web-server.test ``` ### Run other commands in the container You can run other commands in `my-web-server` by using the `container exec` command. To list the files under the content directory, run an `ls` command: ```shellsession % container exec my-web-server ls /content index.html logo.jpg % ``` If you want to poke around in the container, run a shell and issue one or more commands: ```shellsession % container exec --tty --interactive my-web-server bash root@my-web-server:/content# ls index.html logo.jpg root@my-web-server:/content# uname -a Linux my-web-server 6.1.68 #1 SMP Mon Mar 31 18:27:51 UTC 2025 aarch64 GNU/Linux root@my-web-server:/content# exit exit% ``` The `--tty` and `--interactive` flag allow you to interact with the shell from your host terminal. The `--tty` flag tells the shell in the container that its input is a terminal device, and the `--interacive` flag connects what you input in your host terminal to the input of the shell in the container. You will often see these two options abbreviated and specified together as `-ti` or `-it`. ### Access the web server from another container Your web server is accessible from other containers as well as from your host. Launch a second container using your `web-test` image, and this time, specify a `curl` command to retrieve the `index.html` content from the first container. ```shellsession % container run -it --rm web-test curl http://192.168.64.3 Hello

Hello, world!

% ``` If you set up the `test` domain earlier, you can achieve the same result with: ```bash container run -it --rm web-test curl http://my-web-server.test ``` ## Run a published image Push your image to a container registry, publishing it so that you and others can use it. ### Publish the web server image To publish your image, you need push images to a registry service that stores the image for future use. Typically, you need to authenticate with a registry to push an image. This example assumes that you have an account at a hypothetical registry named `registry.example.com` with username `fido` and a password or token `my-secret`, and that your personal repository name is the same as your username. To sign into a secure registry with your login credentials, enter your username and password at the prompts after running: ```bash container registry login registry.example.com ``` Create another name for your image that includes the registry name, your repository name, and the image name, with the tag `latest`: ```bash container images tag web-test registry.example.com/fido/web-test:latest ``` Then, push the image: ```bash container images push registry.example.com/fido/web-test:latest ``` ### Pull and run your image To validate your published image, remove your existing web server image, and then run using the remote image: ```bash container images delete web-test registry.example.com/fido/web-test:latest container run --name my-web-server --dns-domain test --detach --rm registry.example.com/fido/web-test:latest ``` ## Clean up Stop your container and shut down the application. ### Shut down the web server Stop your web server container with: ```bash container stop my-web-server ``` If you list all running and stopped containers, you will see that the `--rm` flag you supplied with the `container run` command caused the container to be removed: ```bash % container ls --all ID IMAGE OS ARCH STATE ADDR buildkit ghcr.io/apple/container-builder-shim/builder:2.1.1 linux arm64 running 192.168.64.2 % ``` To shut down and remove all containers, run: ```bash container rm --all --force ``` ### Stop the container service When you want to stop `container` completely, run: ```bash container system stop ```