Migration Guide

Migrating from FreeIPA.

Move your users, groups, and SSH keys out of FreeIPA and into Dirless with one export command and one import command. UIDs and GIDs are preserved, so file ownership on your hosts stays intact.

On this page
Overview

What migrates

FreeIPADirless
Users (username, UID, GID, GECOS, home, shell, email) Local users, IDs preserved exactly
SSH public keys (ipaSshPubKey) Per-user SSH keys, served to every enrolled host
POSIX groups and memberships Local groups, GIDs and members preserved
User private groups Kept, so each user's primary GID resolves to a name
Disabled accounts (nsAccountLock) Skipped by default (--include-disabled to keep them)
Built-ins (admin, admins, editors, ipausers, ...) Excluded automatically
Passwords do not migrate. FreeIPA stores Kerberos credentials that cannot be exported in a reusable form. Dirless authenticates users with SSH keys (imported automatically) or short-lived SSH certificates - most teams treat the migration as the moment they finish moving off password-based SSH.

Step 1

Export from FreeIPA

On your FreeIPA server, export the accounts subtree to an LDIF file with ldapsearch. Authenticate either with a Kerberos ticket or as the Directory Manager - both produce identical output.

Option A - Kerberos (no Directory Manager password needed)

Shell (FreeIPA server)
kinit admin
ldapsearch -Y GSSAPI -o ldif-wrap=no \
  -b "cn=accounts,$(grep ^basedn /etc/ipa/default.conf | cut -d' ' -f3)" \
  '(|(objectClass=posixaccount)(objectClass=posixgroup))' \
  uid uidNumber gidNumber cn gecos homeDirectory loginShell mail \
  ipaSshPubKey nsAccountLock member memberUid objectClass \
  > freeipa-export.ldif

Option B - Directory Manager

Shell (FreeIPA server)
ldapsearch -x -D 'cn=Directory Manager' -W -o ldif-wrap=no \
  -b 'cn=accounts,dc=example,dc=com' \
  '(|(objectClass=posixaccount)(objectClass=posixgroup))' \
  uid uidNumber gidNumber cn gecos homeDirectory loginShell mail \
  ipaSshPubKey nsAccountLock member memberUid objectClass \
  > freeipa-export.ldif

Replace dc=example,dc=com with your base DN (it is the basedn line in /etc/ipa/default.conf).

Why not ipa user-find? We tested it so you don't have to: ipa user-find --all --raw prints SSH public keys base64-encoded but formatted like plain text, so a parser cannot reliably tell encoded values from literal ones. The ldapsearch LDIF output has no such ambiguity, which is why the importer consumes LDIF.
Keep the export file safe and delete it after the migration. It contains no passwords, but it is a complete listing of your users, groups, and SSH public keys.

Step 2

Dry run

Copy freeipa-export.ldif to any host already enrolled with dirless-cli enroll, then preview what would be imported. A dry run never contacts the backend.

Shell (enrolled host)
dirless-cli import-freeipa --ldif freeipa-export.ldif --dry-run

The output lists every user, group, and skipped entry:

Output
Parsed FreeIPA export:
  Users  : 3 (1 disabled, skipped)
  Groups : 5
    alice uid=11200003 gid=11200003 /bin/zsh (2 ssh keys)
    bob uid=11200004 gid=11200004 /bin/sh
    carol [disabled] uid=11200005 gid=11200005 /bin/sh
    %devs gid=11200006 members=[alice, bob]
    %ops-team gid=11200007 members=[alice]
  Skipped entries:
    - admin: FreeIPA built-in account (excluded)
    - admins: FreeIPA management group (excluded)
    - editors: FreeIPA management group (excluded)
    - Default SMB Group: FreeIPA management group (excluded)

Step 3

Import into Dirless

Run the same command without --dry-run. On an enrolled host the backend URL, credentials, and your age key are all read from /etc/dirless/ - no extra flags needed.

Shell (enrolled host)
dirless-cli import-freeipa --ldif freeipa-export.ldif
Output
Fetching existing local snapshot from https://acme.dirless.com...
  none yet
Pushing 2 local user(s), 5 group(s)...

✓ Import complete.
  Imported : 2 user(s)
  Total    : 2 local user(s), 5 group(s)

Enrolled hosts will pick the users up on their next agent sync (~60s).

The importer fetches your existing portal-managed users, merges the FreeIPA users in, encrypts the result to your age key locally, and pushes it back. Like everything else in Dirless, the server only ever sees an encrypted blob.

Merge behaviour

Users you have already created in the portal are never touched: a FreeIPA user with the same username is skipped with a warning. Re-running the import is safe and idempotent. Pass --overwrite-existing if you want the FreeIPA data to win instead.


Step 4

Verify

Within one sync cycle (60 seconds by default) every enrolled host sees the imported users:

Shell (any enrolled host)
getent passwd alice
alice:x:11200003:11200003:Alice Anderson:/home/alice:/bin/zsh

getent group devs
devs:x:11200006:alice,bob

dirless-cli list-users

The users also appear in the portal under Directory → Local users, where you can edit them, manage their SSH keys, or add host-based access rules using the imported group names. Once you are satisfied, point your hosts away from SSSD and decommission the FreeIPA servers - Dirless has no replication infrastructure to keep on the critical path, so there is nothing to stand up in their place.


Reference

Command options

FlagEffect
--ldif FILE LDIF export from FreeIPA (required)
--dry-run Parse and report only - never contacts the backend
--overwrite-existing Replace local users that already exist (default: skip them)
--include-disabled Import accounts FreeIPA has disabled (default: skip them)
--config PATH Agent config file (default: /etc/dirless/dirless-agent.toml)
--server / --token / --tenant-id / --age-key Run from a non-enrolled machine by passing the backend URL, bearer token, tenant ID, and age private key explicitly
UID ranges. FreeIPA allocates UIDs from a random high range (typically 100 million+), which lands safely above the UID space Dirless uses for cloud-synced users. If your FreeIPA deployment uses custom UIDs below 100000, the importer prints a warning so you can check for collisions before the users go live.

What's next