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

# Docker and Docker Compose

> Give Docker containers persistent, shared storage with an Archil disk, using a bind mount from the host or a mount inside the container.

A Docker volume lives on one host's disk, so its data goes away with the host and other hosts can't reach it. An Archil
disk gives your containers storage that outlives every container and host, and that many containers on many hosts can use
at the same time.

Archil doesn't ship a Docker volume plugin. Instead, use the `archil` client in one of two ways:

* **[Mount on the host and bind into containers](#mount-on-the-host-and-bind-into-containers).** The containers need no
  extra privileges. Use this when you control the host.
* **[Mount inside the container](#mount-inside-the-container).** Each container mounts the disk itself, which needs
  `/dev/fuse` and the `SYS_ADMIN` capability. Use this when the container should carry its own mount, such as replicas
  spread across hosts.

You need a disk, its region, and a [disk token](https://docs.archil.com/concepts/authorization.md#token-based-disk-authorization). To create a
token, open the disk in the Archil console, select **Connections**, click **Add User**, and select **Disk Token**.

## Mount on the host and bind into containers

Install the client on the host and mount the disk, as described in [Mount on Linux](https://docs.archil.com/mounting/linux.md):

```bash theme={null}
curl -s https://archil.com/install | sh
sudo mkdir -p /mnt/archil
export ARCHIL_MOUNT_TOKEN="<disk-token>"
sudo --preserve-env=ARCHIL_MOUNT_TOKEN archil mount <disk-name> /mnt/archil --region <disk-region>
```

Then bind the mountpoint into your containers with `rslave` propagation:

```yaml compose.yaml theme={null}
services:
  app:
    image: alpine:3.20
    command: ["sleep", "infinity"]
    volumes:
      - type: bind
        source: /mnt/archil
        target: /data
        bind:
          propagation: rslave
```

```bash theme={null}
docker compose up -d
docker compose exec app ls /data
```

With `docker run`, add `:rslave` to the volume:

```bash theme={null}
docker run --rm -v /mnt/archil:/data:rslave alpine:3.20 ls /data
```

`rslave` makes the container follow the host's mounts. Without it, Docker takes a private copy of the bind mount when the
container starts:

* A container that starts before the disk is mounted sees an empty directory until you restart it.
* A container that starts while the disk is mounted keeps its own copy of the mount, and `archil unmount` on the host
  waits until that container stops.

With `rslave`, the disk appears in running containers as soon as the host mounts it, and a remount on the host shows up
without restarting them. While the disk is unmounted, `/data` is the empty directory underneath.

Every container on the host shares the host's single mount, so any number of containers on one host can read and write
the disk with no extra flags.

> [!WARNING]
>
> Docker's `read_only` (`:ro`) option doesn't apply to a mount that propagates in after the container starts, so a
> container can still write to the disk. To give a container read-only access, mount the disk inside it with
> `--read-only`.

## Mount inside the container

The container needs three things to mount a disk:

* The `/dev/fuse` device.
* The `SYS_ADMIN` capability, which allows the `mount` call.
* On hosts that use AppArmor, such as Ubuntu and Debian, `apparmor:unconfined`. Docker's default AppArmor profile blocks
  `mount`.

This is a smaller grant than `--privileged`.

Install the client in your image:

```dockerfile Dockerfile theme={null}
FROM debian:bookworm-slim
RUN apt-get update \
 && apt-get install -y --no-install-recommends curl ca-certificates \
 && curl -fsSL https://archil.com/install | sh \
 && rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["sleep", "infinity"]
```

The entrypoint reads the token, mounts the disk, and runs your command. When Docker stops the container, it stops your
command and unmounts the disk before exiting:

```sh entrypoint.sh theme={null}
#!/bin/sh
set -eu

if [ -z "${ARCHIL_MOUNT_TOKEN:-}" ] && [ -f /run/secrets/archil_mount_token ]; then
  ARCHIL_MOUNT_TOKEN="$(cat /run/secrets/archil_mount_token)"
  export ARCHIL_MOUNT_TOKEN
fi

mkdir -p /mnt/archil
archil mount "$ARCHIL_DISK" /mnt/archil --region "$ARCHIL_REGION" ${ARCHIL_MOUNT_FLAGS:-}

"$@" &
app=$!
trap 'kill -TERM "$app" 2>/dev/null' TERM INT

status=0
wait "$app" || status=$?
if kill -0 "$app" 2>/dev/null; then
  wait "$app" || status=$?
fi

archil unmount /mnt/archil
exit "$status"
```

Make it executable with `chmod +x entrypoint.sh`. The Compose file grants the device and capability, and passes the token
as a Docker secret:

```yaml compose.yaml theme={null}
services:
  app:
    build: .
    devices:
      - /dev/fuse
    cap_add:
      - SYS_ADMIN
    security_opt:
      - apparmor:unconfined
    environment:
      ARCHIL_DISK: ${ARCHIL_DISK}
      ARCHIL_REGION: ${ARCHIL_REGION}
      ARCHIL_MOUNT_FLAGS: ${ARCHIL_MOUNT_FLAGS:-}
    secrets:
      - archil_mount_token
    stop_grace_period: 1m

secrets:
  archil_mount_token:
    file: ./archil_mount_token.txt
```

Keep the token file out of the build context:

```text .dockerignore theme={null}
archil_mount_token.txt
.env
```

Save your disk token in `archil_mount_token.txt`, then start the service:

```bash theme={null}
chmod 600 archil_mount_token.txt
ARCHIL_DISK=<disk-name> ARCHIL_REGION=<disk-region> docker compose up -d --build
docker compose exec app ls /mnt/archil
```

> [!NOTE]
>
> You can also put `ARCHIL_DISK` and `ARCHIL_REGION` in a `.env` file next to `compose.yaml`.

## Credentials

Keep disk tokens out of your images. Don't put them in a Dockerfile `ENV` or `ARG`, and don't `COPY` them into the image.

* **Docker secrets** (recommended). Compose mounts the file at `/run/secrets/archil_mount_token`, and the entrypoint above
  reads it. The token doesn't appear in `docker inspect`.
* **Environment variable.** Pass `ARCHIL_MOUNT_TOKEN` from the shell that runs Compose. This is simpler, but anyone who
  can run `docker inspect` on the container can read the token.

  ```yaml compose.yaml theme={null}
  services:
    app:
      environment:
        ARCHIL_MOUNT_TOKEN: ${ARCHIL_MOUNT_TOKEN}
  ```

For a host mount, only the host needs the token. The containers never see it.

## Share one disk across containers and hosts

On a single host, mount the disk once on the host and bind it into every container that needs it, as shown
[above](#mount-on-the-host-and-bind-into-containers).

When several containers or hosts each mount the disk, choose a mount mode. By default, `archil mount` takes exclusive
ownership of the disk, so only one client can mount it for writing. To let many clients mount it at once, use one of:

* `--conditional`: every client can write anywhere on the disk without `archil checkout`. Use this when several replicas
  write to the same disk.
* `--shared`: the disk is read-only to each client until it runs `archil checkout <path>`, which gives that client
  exclusive write access to the path.

The entrypoint above passes `ARCHIL_MOUNT_FLAGS` to `archil mount`. To run two replicas of the service against one disk:

```bash theme={null}
ARCHIL_MOUNT_FLAGS=--conditional docker compose up -d --scale app=2
```

With `--shared`, check out a directory before writing to it, and check it in when you're done:

```bash theme={null}
docker compose exec --index 1 app sh -c '
  mkdir -p /mnt/archil/worker-1 &&
  archil checkout /mnt/archil/worker-1 &&
  echo hello > /mnt/archil/worker-1/out.txt &&
  archil checkin /mnt/archil/worker-1'
```

Each client caches file and directory information, so a file that one client creates can take up to 30 seconds to appear
on another. See [Consistency](https://docs.archil.com/details/consistency.md) for how to tune this, and [Sharing disks](https://docs.archil.com/concepts/sharing-disks.md) for
how the modes compare and how they handle two clients writing the same file.

## Shut down cleanly

Always unmount with `archil unmount`, which flushes data that hasn't reached the disk yet and releases the disk for other
clients.

* **Mounted inside the container.** `docker stop` and `docker compose down` send `SIGTERM` only to the container's main
  process, and everything else in the container is killed when it exits. The entrypoint above handles `SIGTERM` by
  stopping your command and running `archil unmount` first. Set `stop_grace_period` long enough for the unmount to
  finish.
* **Mounted on the host.** Stop the containers first, then unmount:

  ```bash theme={null}
  docker compose down
  sudo archil unmount /mnt/archil
  ```

  A container whose bind mount doesn't use `rslave` keeps the disk mounted, and `archil unmount` waits until it stops.


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