# IcontainU

> A native SwiftUI macOS GUI for Apple's container — smart container creation, one-click Compose, and a built-in MCP server.

- **Type:** MCP server
- **Install:** `agentstack add mcp-jeffqiu1989-icontainu`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [jeffqiu1989](https://agentstack.voostack.com/s/jeffqiu1989)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [jeffqiu1989](https://github.com/jeffqiu1989)
- **Source:** https://github.com/jeffqiu1989/IcontainU
- **Website:** https://jeffqiu1989.github.io/IcontainU/

## Install

```sh
agentstack add mcp-jeffqiu1989-icontainu
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# IcontainU

**A native macOS GUI for Apple's [`container`](https://github.com/apple/container).**

*`I`* — Apple's lowercase‑i lineage (iOS, iPhone) · *`contain`* — container · *`U`* — UI

Built with SwiftUI. No Electron, no daemon of its own — it just drives the `container` you already have.

English | [中文](README_zh.md)

---

## Highlights

Two things make IcontainU worth your dock:

| ⚡ Smart Create | 🧩 One‑click Compose |
| --- | --- |
|  |  |
| **Drop in an image, the form fills itself.** Ports, mounts, and the env vars the entrypoint *actually needs* (like `MYSQL_ROOT_PASSWORD`) are read from the image and pre‑filled. No more copying `docker run` snippets. | **Bring a whole stack up in one click.** Import a `compose.yaml` and Up the project in dependency order, with `healthcheck` gating. Projects persist across `down` and restarts — and it works around Apple `container`'s broken container‑to‑container DNS so service names just resolve. |

## More features

- **🐧 Machines that just work** — presets pointing at official *init‑ready* images (Alpine, Rocky UBI‑init), so machines actually boot. CPU, memory, and home‑mount are all settable.
- **📦 Smart image pull** — pulls only your host architecture, and is registry‑mirror aware with a one‑click **DaoCloud preset** (9 registries, individually toggleable) that leaves no trace on local images.
- **🔨 Image building** - build container images from a Dockerfile via the shared BuildKit builder, with no-cache toggle, live build log, and per-config build history.
- **🃏 Everything on a card** — Start / Stop / Shell / Logs / Delete per container, plus a live **stats** tab and streaming logs.
- **✨ Friction removers** — tap to copy an IP or `ip:port`, tap a mount to open it in Finder, local‑image autocomplete, and Docker‑style auto‑naming.
- **🚀 Frictionless setup** — first launch auto‑installs the kernel and monitors `container` health for you.
- **🤖 MCP server** - a built-in [Model Context Protocol](https://modelcontextprotocol.io) server exposes every container, image, machine, volume, network, and Compose operation as a tool, so Claude Code, OpenCode, or any MCP client can drive IcontainU remotely over HTTP with a Bearer API key. See [docs/mcp_server.md](docs/mcp_server.md) for the full 25-tool API.

## Screenshots

| Containers | Create a container |
| --- | --- |
|  |  |

| Create a machine | Images |
| --- | --- |
|  |  |

| Registry mirrors | Built-in MCP server |
| --- | --- |
|  |  |

## Requirements

- **Apple silicon** Mac (M‑series), **macOS 26** or newer
- Apple [`container`](https://github.com/apple/container/releases) ≥ 1.0.0 installed

Start the system once from the CLI so it installs the kernels (faster than the in‑app download):

```bash
container system start
container system status   # should report: running
```

Without a running `container` system the app still opens, but the sidebar stays empty.

## Download & install

Download `IcontainU-v0.5.0.zip` from [Releases](../../releases), unzip, and move `IcontainU.app` to Applications.

Not notarized — on first launch, right‑click → Open, or run:

```bash
xattr -d com.apple.quarantine /Applications/IcontainU.app
```

## Build from source

Requires the Swift 6.2 toolchain (Xcode 26).

```bash
swift build && swift run IcontainU
# package a signed .app:
./scripts/package-app.sh
```

## Status & known limitations

**0.5.0** — early, but useful day to day.

- `Shell` opens Terminal.app — no embedded terminal yet.
- System configuration is **view‑only** in the app; edit it via the CLI.
- Menu bar support is under development.
- Compose does not support `build:` - use the standalone **Build** feature to build images, then reference them by tag in `image:`.

## Compose reference

IcontainU supports a **practical subset** of the Compose spec. Anything unsupported is surfaced as a warning banner at import — **never silently dropped**.

Supported fields &amp; what's not

| Field | Notes |
| --- | --- |
| `image` | — |
| `command` | string **or** array form |
| `ports` | numeric and `host:container/proto` |
| `environment` | list `["K=V"]` **and** map `{K: V}` |
| `volumes` | named (`vol:/data`) and bind (`/host:/data[:ro]`, incl. relative `./`) |
| `networks` | per‑service and top‑level |
| `depends_on` | start order **and** `condition: service_healthy` |
| `healthcheck` | `test`, `interval`, `timeout`, `retries`, `start_period` |
| `container_name`, `user` | — |
| top‑level `networks:` / `volumes:` | — |

Plus `${VAR}` / `.env` interpolation at parse time.

**Not supported:**

| Field | Notes |
| --- | --- |
| `build:` | — |
| `restart:` | — |
| `deploy.replicas` / scale | — |
| `env_file` | — |
| `profiles` | — |
| `secrets` | — |
| `configs` | — |
| `extends` | — |
| YAML anchors | — |
| advanced `driver_opts` | — |

Project isolation &amp; multi‑network

- **Every project is namespaced.** Containers, volumes and networks are prefixed with the project name, so two projects that each declare a `db` service run side by side. Pinning the **same** `container_name:` in two projects fails loudly instead of hijacking.
- **Multi‑network is fully supported.** A service on several networks resolves each peer on a network the two containers actually share.

Healthcheck gating — how service_healthy is honored

Apple `container` 1.0.0 has no native healthcheck, so IcontainU runs the probe via `container exec` **during Up** to gate `depends_on: { condition: service_healthy }`. There is no always‑on healthy/unhealthy badge.

- If a gated dependency never becomes healthy, Up fails but the dependency is left running so its logs explain why — fix the compose file and re‑Up.
- A `service_healthy` dependency with **no** healthcheck warns and is treated as start‑order only.

Runtime constraints — Apple container on macOS 26 (not IcontainU bugs)

- **Container‑to‑container DNS is broken on macOS 26 — IcontainU works around it** by injecting ` → real IP` into each container's `/etc/hosts` after Up.
- **Database data dirs on bind mounts — the chown problem and how to fix it.**

  On macOS, the bind-mount root is owned by the host user and refuses `chown`. Images that `chown` their data directory at startup fail with "Operation not permitted" when the data directory *is* the mount-point root:

  ```
  # MySQL / MariaDB
  chown: changing ownership of '/var/lib/mysql/': Operation not permitted

  # PostgreSQL ≤17
  chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted
  chown: changing ownership of '/var/lib/postgresql/data': Operation not permitted
  ```

  **Affected versions:**
  - **PostgreSQL 17 and below** — `PGDATA` defaults to `/var/lib/postgresql/data`, which is the mount-point root → fails.
  - **PostgreSQL 18+** — `PGDATA` defaults to a subdirectory (`/var/lib/postgresql/18/docker`) → no issue, works out of the box.
  - **MySQL / MariaDB** — all versions are affected.

  **Fix:** point the data directory at a *subdirectory* of the mount instead. The image-specific setting that controls this differs per image:

  | Image | Fix | Example compose snippet |
  | --- | --- | --- |
  | PostgreSQL ≤17 | Set `PGDATA` to a subdirectory | `environment: PGDATA: /var/lib/postgresql/data/pgdata` |
  | MySQL / MariaDB | Pass `--datadir` pointing to a subdirectory | `command: ["--datadir", "/var/lib/mysql/data"]` |

  Ready-to-use templates with the fix pre-applied are in [`samples/`](samples/):
  - [`template-postgres-17.yaml`](samples/template-postgres-17.yaml) — PostgreSQL 17 with bind-mounted data dir
  - [`template-mysql.yaml`](samples/template-mysql.yaml) — MySQL 8 with bind-mounted data dir

  Import via **Compose → New Project**, edit the password and host path, then Up.
- **Non‑root images need `user: "0"` on a named volume**, otherwise they can't write their data dir.

## Samples

The [`samples/`](samples/) directory ships Compose templates you can import via **Compose → New Project** and Up directly. Each one is verified to run on IcontainU.

| Stack | Services | Notes |
|---|---|---|
| [postgresql-pgadmin](samples/postgresql-pgadmin/) | PostgreSQL + pgAdmin | `${VAR}` placeholder template — edit passwords before import |
| [gitea-postgres](samples/gitea-postgres/) | Gitea + PostgreSQL | Named volumes, `restart: always` |
| [nextcloud-postgres](samples/nextcloud-postgres/) | Nextcloud + PostgreSQL | |
| [nextcloud-redis-mariadb](samples/nextcloud-redis-mariadb/) | Nextcloud + Redis + MariaDB | **Multi-network** demo (separate dbnet / redisnet) |
| [wordpress-mysql](samples/wordpress-mysql/) | WordPress + MariaDB | |
| [elasticsearch-logstash-kibana](samples/elasticsearch-logstash-kibana/) | ELK stack (ES 8 + Logstash + Kibana) | Full `healthcheck` + `depends_on: condition: service_healthy` |
| [prometheus-grafana](samples/prometheus-grafana/) | Prometheus + Grafana | `user: "0"` for named-volume write; bind mounts for config |
| [postgres-healthcheck](samples/postgres-healthcheck/) | PostgreSQL + Alpine | Minimal healthcheck / `service_healthy` demo |
| [kafka-cluster-kraft](samples/kafka-cluster-kraft/) | Kafka 4.3.1 KRaft (3 controllers + 3 brokers) | Advertised listeners on hostnames; depends_on startup |
| [redis-cluster](samples/redis-cluster/) | Redis 7 cluster | 3-master / 3-replica with auto-failover; `cluster-announce-hostname` + `/etc/hosts` DNS workaround |
| [redis-cluster-envoy-proxy](samples/redis-cluster-envoy-proxy/) | Redis cluster + Envoy proxy | Slot-aware Envoy proxy at `host:6379` — plain `redis-cli`, no `-c` flag needed |
| [`template-mysql.yaml`](samples/template-mysql.yaml) | MySQL 8 (single) | Standalone template; bind-mount data dir with `--datadir` subdirectory (macOS chown fix) |
| [`template-postgres-17.yaml`](samples/template-postgres-17.yaml) | PostgreSQL 17 (single) | Standalone template; `PGDATA` subdirectory bind-mount (macOS chown fix) |

> **macOS bind-mount note:** some samples use bind-mounted data directories. On macOS the mount root is owned by the host user and can't be `chown`'d. Images that need `chown` on their data dir (MySQL, PostgreSQL ≤ 17) are pointed at a **subdirectory** via `--datadir` / `PGDATA` — the templates already handle this. See the [Compose reference](#compose-reference) for details.

## MCP server

IcontainU ships an embedded [Model Context Protocol](https://modelcontextprotocol.io) server, so an AI client can operate containers, images, machines, volumes, networks, and Compose projects on your behalf - useful for "bring this stack up and verify it's healthy" workflows driven from Claude Code, OpenCode, or any MCP-compatible client.

- **Transport**: MCP over Streamable HTTP at `/mcp` (swift-nio server, default port `3000`).
- **Auth**: Bearer API key, generated and managed in the in-app **MCP** panel. Constant-time comparison; no key, no access.
- **Bind**: `127.0.0.1` (localhost only) by default; switch to `0.0.0.0` in the panel to reach it from the LAN.

**25 tools** across 6 resource groups:

| Resource | Tools |
| --- | --- |
| Container | `list`, `create`, `start`, `stop`, `delete`, `exec`, `logs`, `inspect` |
| Image | `list`, `pull`, `delete` |
| Machine | `list`, `boot`, `stop`, `delete` |
| Volume | `list`, `create`, `delete` |
| Network | `list`, `create`, `delete` |
| Compose | `list`, `up`, `down`, `status` |

Full tool schemas, parameters, and examples: [docs/mcp_server.md](docs/mcp_server.md).

Quick start from a client config (`.mcp.json`):

```json
{
  "mcpServers": {
    "containers": {
      "url": "http://127.0.0.1:3000/mcp",
      "headers": { "Authorization": "Bearer " }
    }
  }
}
```
## License & acknowledgements

Licensed under the Apache License 2.0 — see [LICENSE](LICENSE). Built on Apple's `container` and `containerization`; see [NOTICE](NOTICE) for attribution.

Developed with the assistance of vibe coding (AI-assisted development).

Thanks to the [LINUX DO](https://linux.do) community for providing a platform for communication and learning.

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jeffqiu1989](https://github.com/jeffqiu1989)
- **Source:** [jeffqiu1989/IcontainU](https://github.com/jeffqiu1989/IcontainU)
- **License:** Apache-2.0
- **Homepage:** https://jeffqiu1989.github.io/IcontainU/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-jeffqiu1989-icontainu
- Seller: https://agentstack.voostack.com/s/jeffqiu1989
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
