# Migrating from using `clanModules` to `clanServices`

**Audience**: This is a guide for **people using `clanModules`**.
If you are a **module author** and need to migrate your modules please consult our **new** [clanServices authoring guide](../services/community.md)

## What's Changing?

Clan is transitioning from the legacy `clanModules` system to the `clanServices` system. This guide will help you migrate your service definitions from the old format (`inventory.services`) to the new format (`inventory.instances`).

| Feature          | `clanModules` (Old)        | `clanServices` (New)    |
| ---------------- | -------------------------- | ----------------------- |
| Module Class     | `"nixos"`                  | `"clan.service"`        |
| Inventory Key    | `services`                 | `instances`             |
| Module Source    | Static                     | Composable via flakes   |
| Custom Settings  | Loosely structured         | Strongly typed per-role |
| Migration Status | Deprecated (to be removed) | ✅ Preferred             |

---

## Before: Old `services` Definition

```nix
services = {
    admin = {
        simple = {
            roles.default.tags = [ "all" ];

            roles.default.config = {
                allowedKeys = {
                    "key-1" = "ssh-ed25519 AAAA...0J jon@jon-os";
                };
            };
        };
    };
};
```

### Complex Example: Multi-service Setup

```nix
# Old format
services = {
    borgbackup.production = {
        roles.server.machines = [ "backup-server" ];
        roles.server.config = {
            directory = "/var/backup/borg";
        };
        roles.client.tags = [ "backup" ];
        roles.client.extraModules = [ "nixosModules/borgbackup.nix" ];
    };

    zerotier.company-network = {
        roles.controller.machines = [ "network-controller" ];
        roles.moon.machines = [ "moon-1" "moon-2" ];
        roles.peer.tags = [ "nixos" ];
    };

    sshd.internal = {
        roles.server.tags = [ "nixos" ];
        roles.client.tags = [ "nixos" ];
        config.certificate.searchDomains = [
            "internal.example.com"
            "vpn.example.com"
        ];
    };
};
```

---

## ✅ After: New `instances` Definition with `clanServices`

```nix
instances = {
    # The instance_name is arbitrary but must be unique
    # We recommend to incorporate the module name in some kind to keep it clear
    admin-simple = {
        module = {
            name = "admin";
            input = "clan-core";
        };

        roles.default.tags."all" = {};

        # Move settings either into the desired role
        # In that case they effect all 'client-machines'
        roles.default.settings = {
            allowedKeys = {
                "key-1" = "ssh-ed25519 AAAA...0J jon@jon-os";
            };
        };
        # ----------------------------
        # OR move settings into the machine
        # then they affect only that single 'machine'
        roles.default.machines."jon".settings = {
            allowedKeys = {
                "key-1" = "ssh-ed25519 AAAA...0J jon@jon-os";
            };
        };
    };
};
```

### Complex Example Migrated

```nix
# New format
instances = {
    borgbackup-production = {
        module = {
            name = "borgbackup";
            input = "clan-core";
        };
        roles.server.machines."backup-server" = { };
        roles.server.settings = {
            directory = "/var/backup/borg";
        };
        roles.client.tags.backup = { };
        roles.client.extraModules = [ ../nixosModules/borgbackup.nix ];
    };

    zerotier-company-network = {
        module = {
            name = "zerotier";
            input = "clan-core";
        };
        roles.controller.machines."network-controller" = { };
        roles.moon.machines."moon-1".settings = {
            stableEndpoints = [ "10.0.0.1" "2001:db8::1" ];
        };
        roles.moon.machines."moon-2".settings = {
            stableEndpoints = [ "10.0.0.2" "2001:db8::2" ];
        };
        roles.peer.tags.nixos = { };
    };

    sshd-internal = {
        module = {
            name = "sshd";
            input = "clan-core";
        };
        roles.server.tags.nixos = { };
        roles.client.tags.nixos = { };
        roles.client.settings = {
            certificate.searchDomains = [
                "internal.example.com"
                "vpn.example.com"
            ];
        };
    };
};
```

---

## Steps to Migrate

### Move `services` entries to `instances`

Check if a service that you use has been migrated [In our reference](../../reference/clanServices/index.md)

In your inventory, move it from:

```nix
services = { ... };
```

to:

```nix
instances = { ... };
```

Each nested service-instance-pair becomes a flat key, like `borgbackup.simple → borgbackup-simple`.

---

### Add `module.name` and `module.input`

Each instance must declare the module name and flake input it comes from:

```nix
module = {
  name = "borgbackup";
  input = "clan-core";  # The name of your flake input
};
```

If you used `clan-core` as an input:

```nix
inputs.clan-core.url = "github:clan/clan-core";
```

Then refer to it as `input = "clan-core"`.

---

### Move role and machine config under `roles`

In the new system:

* Use `roles.<role>.machines.<hostname>.settings` for machine-specific config.
* Use `roles.<role>.settings` for role-wide config.
* Remove: `.config` as a top-level attribute is removed.

Example:

```nix
roles.default.machines."test-inventory-machine".settings = {
  packages = [ "hello" ];
};
```

### Important Type Changes

The new `instances` format uses **attribute sets** instead of **lists** for tags and machines:

```nix
# ❌ Old format (lists)
roles.client.tags = [ "backup" ];
roles.server.machines = [ "blob64" ];

# ✅ New format (attribute sets)
roles.client.tags.backup = { };
roles.server.machines.blob64 = { };
```

### Handling Multiple Machines/Tags

When you need to assign multiple machines or tags to a role:

```nix
# ❌ Old format
roles.moon.machines = [ "eva" "eve" ];

# ✅ New format - each machine gets its own attribute
roles.moon.machines.eva = { };
roles.moon.machines.eve = { };
```

---

## Migration Status of clanModules

The following table shows the migration status of each deprecated clanModule:

| clanModule               | Migration Status                                                  | Notes                                                            |
|--------------------------|-------------------------------------------------------------------|------------------------------------------------------------------|
| `admin`                  | ✅ [Migrated](../../reference/clanServices/admin.md)              |                                                                  |
| `auto-upgrade`           | ❌ Removed                                                        |                                                                  |
| `borgbackup-static`      | ❌ Removed                                                        |                                                                  |
| `borgbackup`             | ✅ [Migrated](../../reference/clanServices/borgbackup.md)         |                                                                  |
| `data-mesher`            | ✅ [Migrated](../../reference/clanServices/data-mesher.md)        |                                                                  |
| `deltachat`              | ❌ Removed                                                        |                                                                  |
| `disk-id`                | ❌ Removed                                                        |                                                                  |
| `dyndns`                 | ✅ [Migrated](../../reference/clanServices/dyndns.md)             |                                                                  |
| `ergochat`               | ❌ Removed                                                        |                                                                  |
| `garage`                 | ✅ [Migrated](../../reference/clanServices/garage.md)             |                                                                  |
| `golem-provider`         | ❌ Removed                                                        |                                                                  |
| `heisenbridge`           | ❌ Removed                                                        |                                                                  |
| `importer`               | ✅ [Migrated](../../reference/clanServices/importer.md)           |                                                                  |
| `iwd`                    | ❌ Removed                                                        | Use [wifi service](../../reference/clanServices/wifi.md) instead |
| `localbackup`            | ✅ [Migrated](../../reference/clanServices/localbackup.md)        |                                                                  |
| `localsend`              | ❌ Removed                                                        |                                                                  |
| `machine-id`             | ✅ [Migrated](../../reference/clan.core/settings.md)              | Now an [option](../../reference/clan.core/settings.md)           |
| `matrix-synapse`         | ✅ [Migrated](../../reference/clanServices/matrix-synapse.md)     |                                                                  |
| `moonlight`              | ❌ Removed                                                        |                                                                  |
| `mumble`                 | ❌ Removed                                                        |                                                                  |
| `mycelium`               | ✅ [Migrated](../../reference/clanServices/mycelium.md)           |                                                                  |
| `nginx`                  | ❌ Removed                                                        |                                                                  |
| `packages`               | ✅ [Migrated](../../reference/clanServices/packages.md)           |                                                                  |
| `postgresql`             | ✅ [Migrated](../../reference/clan.core/settings.md)              | Now an [option](../../reference/clan.core/settings.md)           |
| `root-password`          | ✅ [Migrated](../../reference/clanServices/users.md)              | See [migration guide](../../reference/clanServices/users.md#migration-from-root-password-module) |
| `single-disk`            | ❌ Removed                                                        |                                                                  |
| `sshd`                   | ✅ [Migrated](../../reference/clanServices/sshd.md)               |                                                                  |
| `state-version`          | ✅ [Migrated](../../reference/clan.core/settings.md)              | Now an [option](../../reference/clan.core/settings.md)           |
| `static-hosts`           | ❌ Removed                                                        |                                                                  |
| `sunshine`               | ❌ Removed                                                        |                                                                  |
| `syncthing-static-peers` | ❌ Removed                                                        |                                                                  |
| `syncthing`              | ✅ [Migrated](../../reference/clanServices/syncthing.md)          |                                                                  |
| `thelounge`              | ❌ Removed                                                        |                                                                  |
| `trusted-nix-caches`     | ✅ [Migrated](../../reference/clanServices/trusted-nix-caches.md) |                                                                  |
| `user-password`          | ✅ [Migrated](../../reference/clanServices/users.md)              |                                                                  |
| `vaultwarden`            | ❌ Removed                                                        |                                                                  |
| `xfce`                   | ❌ Removed                                                        |                                                                  |
| `zerotier-static-peers`  | ❌ Removed                                                        |                                                                  |
| `zerotier`               | ✅ [Migrated](../../reference/clanServices/zerotier.md)           |                                                                  |
| `zt-tcp-relay`           | ❌ Removed                                                        |                                                                  |

---

!!! Warning
    * Old `clanModules` (`class = "nixos"`) are deprecated and will be removed in the near future.
    * `inventory.services` is no longer recommended; use `inventory.instances` instead.
    * Module authors should begin exporting service modules under the `clan.modules` attribute of their flake.

## Troubleshooting Common Migration Errors

### Error: "not of type `attribute set of (submodule)`"

This error occurs when using lists instead of attribute sets for tags or machines:

```
error: A definition for option `flake.clan.inventory.instances.borgbackup-blob64.roles.client.tags' is not of type `attribute set of (submodule)'.
```

**Solution**: Convert lists to attribute sets as shown in the "Important Type Changes" section above.

### Error: "unsupported attribute `module`"

This error indicates the module structure is incorrect:

```
error: Module ':anon-4:anon-1' has an unsupported attribute `module'.
```

**Solution**: Ensure the `module` attribute has exactly two fields: `name` and `input`.

### Error: "attribute 'pkgs' missing"

This suggests the instance configuration is trying to use imports incorrectly:

```
error: attribute 'pkgs' missing
```

**Solution**: Use the `module = { name = "..."; input = "..."; }` format instead of `imports`.

### Removed Features

The following features from the old `services` format are no longer supported in `instances`:

- Top-level `config` attribute (use `roles.<role>.settings` instead)
- Direct module imports (use the `module` declaration instead)

### extraModules Support

The `extraModules` attribute is still supported in the new instances format! The key change is how modules are specified:

**Old format (string paths relative to clan root):**
```nix
roles.client.extraModules = [ "nixosModules/borgbackup.nix" ];
```

**New format (NixOS modules):**
```nix
# Direct module reference
roles.client.extraModules = [ ../nixosModules/borgbackup.nix ];

# Or using self
roles.client.extraModules = [ self.nixosModules.borgbackup ];

# Or inline module definition
roles.client.extraModules = [
  {
    # Your module configuration here
  }
];
```

The `extraModules` now expects actual **NixOS modules** rather than string paths. This provides better type checking and more flexibility in how modules are specified.

**Alternative: Using @clan/importer**

For scenarios where you need to import modules with specific tag-based targeting, you can also use the dedicated `@clan/importer` service:

```nix
instances = {
  my-importer = {
    module.name = "@clan/importer";
    module.input = "clan-core";
    roles.default.tags.my-tag = { };
    roles.default.extraModules = [ self.nixosModules.myModule ];
  };
};
```

## Further reference

* [Inventory Concept](../inventory/inventory.md)
* [Authoring a 'clan.service' module](../services/community.md)
* [ClanServices](../inventory/clanServices.md)
