# How to Migrate Containers Between Networks and Troubleshoot IP Assignment in Apple Container

> Learn to migrate containers between networks and troubleshoot IP assignment in Apple Container. Use detachNetwork attachNetwork or CLI commands. Verify CIDR and subnet uniqueness to resolve IP issues.

- Repository: [Apple/container](https://github.com/apple/container)
- Tags: how-to-guide
- Published: 2026-06-21

---

**Migrate containers between networks by disconnecting them from the current network and reconnecting to the target using `ContainerClient.detachNetwork()` and `attachNetwork()`, or the CLI commands `container network disconnect` and `connect`, while troubleshooting IP assignment by verifying CIDR validity and subnet uniqueness in `NetworkConfiguration`.**

The Apple `container` repository implements a full-stack container runtime that communicates with a local **container-apiserver** over XPC. When you need to migrate containers between networks or resolve IP assignment issues, you interact with the `NetworkClient` and `ContainerClient` APIs located in the service layer. This guide explains the architecture and provides practical code examples for network migration and debugging.

## Network Architecture Overview

The networking stack centers on three primary types defined in the service layer. The **`NetworkClient`** class in [`Sources/Services/ContainerAPIService/Client/NetworkClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/NetworkClient.swift) serves as the XPC client for creating, listing, and deleting networks. It exposes methods including `create(configuration:)`, `list()`, `get(id:)`, and `delete(id:)`.

The **`NetworkConfiguration`** struct defines the desired state when creating a network, initialized with parameters for `name`, `mode`, `ipv4Subnet`, `ipv6Subnet`, `labels`, `plugin`, and `options`. The **`NetworkResource`** represents the runtime state returned by the server, containing the assigned `ipv4Address`, `ipv6Address`, MAC address, and the `isBuiltin` flag.

Container network attachments are managed through **`ContainerClient`**, which provides `attachNetwork(containerId:networkId:)` and `detachNetwork(containerId:networkId:)` methods. According to the test references in [`Tests/CLITests/Utilities/CLITest.swift`](https://github.com/apple/container/blob/main/Tests/CLITests/Utilities/CLITest.swift), attachment information is stored in the container’s status as `ContainerResource.Attachment` objects.

## Migrating Containers Between Networks

To migrate a container to a different network, you must detach it from its current network and attach it to the target. The CLI commands `container network disconnect` and `container network connect` are thin wrappers around the XPC routes `networkDisconnect` and `networkConnect`.

### Identify the Target Network

First, locate the destination network using `NetworkClient.list()` or create a new one. You can inspect existing networks to verify subnet availability and avoid conflicts.

```swift
let client = NetworkClient()
let networks = try await client.list()
for net in networks {
    print("Network: \(net.name) – Subnet: \(net.ipv4Subnet?.description ?? "none")")
}

```

### Detach from the Current Network

Before attaching to a new network, disconnect the container from its existing one. While stopping the container is optional, it ensures reliable detachment. Use `ContainerClient.detachNetwork(containerId:networkId:)` or the CLI:

```bash
container network disconnect my-container default

```

This sends the `networkDisconnect` route to the server and updates the container’s `networks` array in its status record.

### Attach to the New Network

Connect the container to the target network using `ContainerClient.attachNetwork(containerId:networkId:)`. The server allocates a fresh IP from the network’s subnet via the `container-network-vmnet` plugin.

```bash
container network connect my-container backend

```

If using the Swift API directly:

```swift
let containerClient = ContainerClient()
try await containerClient.detachNetwork(containerId: "my-container", networkId: "default")
try await containerClient.attachNetwork(containerId: "my-container", networkId: "backend")

```

## Troubleshooting IP Assignment

IP allocation occurs inside the `container-network-vmnet` plugin, which respects the CIDR supplied in `NetworkConfiguration.ipv4Subnet` and `ipv6Subnet`. When debugging IP issues, inspect the `NetworkResource` using `NetworkClient.get(id:)` or the CLI `container network inspect`.

### No IP Address Assigned

If `container network connect` succeeds but no IP appears, verify the network configuration contains a valid CIDR. In [`Sources/ContainerResource/Network/NetworkConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Network/NetworkConfiguration.swift), ensure you initialized the configuration with a proper subnet string:

```swift
let config = try NetworkConfiguration(
    name: "frontend",
    mode: .bridge,
    ipv4Subnet: CIDRv4("10.42.0.0/24"),
    ipv6Subnet: nil,
    labels: .init(),
    plugin: "container-network-vmnet",
    options: [:]
)

```

### IP Conflicts and Duplicate Addresses

Overlapping subnets or stale lease records cause conflicts. List all networks and verify subnets are non-overlapping. If conflicts persist, delete and recreate the network to clear stale state.

### Container Shows "none" Network

A container displays `none` network when started with `--network none` or when the default network was improperly removed. Check the container status and re-attach using `container network connect`.

### MAC Address Mismatch

Supplying an invalid `mac` key in the `options` dictionary of `NetworkConfiguration` causes assignment failures. Remove manual MAC entries from the `options` map to let the plugin generate valid addresses automatically.

## Practical Implementation Examples

### Creating a Custom Network

Define a network with specific CIDR blocks using `NetworkConfiguration` and `NetworkClient`:

```swift
import ContainerResource
import Services.ContainerAPIService.Client

let client = NetworkClient()

let config = try NetworkConfiguration(
    name: "backend",
    mode: .bridge,
    ipv4Subnet: CIDRv4("10.99.0.0/24"),
    ipv6Subnet: nil,
    labels: .init(),
    plugin: "container-network-vmnet",
    options: [:]
)

let network = try await client.create(configuration: config)
print("Created network \(network.name) with ID \(network.id)")

```

### Inspecting Network Resources

Retrieve runtime details including assigned IPs:

```swift
let net = try await client.get(id: "backend")
print("Network \(net.name) – IPv4 range: \(net.ipv4Subnet?.description ?? "none")")
print("Attached containers:")
for container in net.attachedContainers {
    print(" – \(container.id): \(container.ipv4Address?.description ?? "none")")
}

```

### Fixing Missing IP Addresses

When a container lacks an IP, verify attachment and re-connect if necessary:

```swift
let containerClient = ContainerClient()
let status = try await containerClient.inspect(id: "my-app")
guard let ip = status.networks.first?.ipv4Address else {
    print("No IPv4 address – re-attaching to network")
    try await containerClient.attachNetwork(containerId: "my-app", networkId: "frontend")
    return
}

```

Note that the **builtin** network (`default`) accessed via `NetworkClient.builtin` cannot be deleted. If a container is attached to it, you must explicitly disconnect it before connecting to other networks.

## Summary

- **Migrate containers** by calling `ContainerClient.detachNetwork()` followed by `attachNetwork()`, or using the CLI `container network disconnect` and `connect` commands.
- **Network configuration** is defined in `NetworkConfiguration` and managed through `NetworkClient` in [`Sources/Services/ContainerAPIService/Client/NetworkClient.swift`](https://github.com/apple/container/blob/main/Sources/Services/ContainerAPIService/Client/NetworkClient.swift).
- **IP assignment** depends on valid CIDR blocks in the network configuration and the `container-network-vmnet` plugin.
- **Troubleshoot** missing IPs by verifying subnet validity, checking for overlaps, and ensuring the `options` dictionary does not contain invalid MAC addresses.
- The **builtin** network cannot be deleted; containers must be explicitly disconnected from it before migrating.

## Frequently Asked Questions

### Can I migrate a container between networks without stopping it?

Yes, though stopping the container is recommended for reliable detachment. You can execute `container network disconnect` and `container network connect` while the container is running, as the `ContainerClient` methods update the network attachment in the container’s status dynamically.

### Why does my container show no IP address after connecting to a network?

This occurs when the `NetworkConfiguration` lacks a valid CIDR or when the `ipv4Subnet` parameter is malformed. Verify the network configuration in [`Sources/ContainerResource/Network/NetworkConfiguration.swift`](https://github.com/apple/container/blob/main/Sources/ContainerResource/Network/NetworkConfiguration.swift) and ensure you passed a valid subnet like `CIDRv4("10.0.1.0/24")` during creation.

### What is the builtin network and can I delete it?

The builtin network (named `default`) is a special `NetworkResource` where `isBuiltin` is true. According to the source code in [`NetworkClient.swift`](https://github.com/apple/container/blob/main/NetworkClient.swift), this network cannot be deleted. If a container is attached to it, you must explicitly disconnect it using `container network disconnect` before attaching to other networks.

### How do I avoid IP conflicts when creating multiple networks?

Ensure each `NetworkConfiguration` uses non-overlapping subnets. Check existing networks using `NetworkClient.list()` to inspect current `ipv4Subnet` ranges. If conflicts arise from stale lease records, delete and recreate the affected network to clear the plugin state.