Identity Realm
    Kerberos + CA

    Deploy FreeIPA on a VPS

    Stand up a full FreeIPA identity realm on a RamNode VPS — 389 Directory Server, MIT Kerberos, Dogtag CA, client enrollment, and replicas on AlmaLinux 10.

    FreeIPA is the full-weight open source identity stack: 389 Directory Server for LDAP, MIT Kerberos for single sign-on, Dogtag for an internal certificate authority, optional BIND for integrated DNS, and a web UI plus CLI on top. If you need Kerberos tickets, host-based access control, centralized sudo rules, machine enrollment, and an internal CA that issues host and service certificates, FreeIPA is the only open source project that ships all of it as one integrated product.

    It is also the heaviest option. Plan the deployment before you type the first command, because the realm name, domain name, and server hostname cannot be changed after installation.

    This guide covers a single-server FreeIPA deployment on a RamNode KVM VPS, then enrolling clients and adding a replica.

    Verified against FreeIPA 4.12.x on AlmaLinux 10. The same steps apply to Rocky Linux 10 and RHEL 10. On EL10 the server packages are plain AppStream RPMs; the idm:DL1 module dance from EL9 and earlier is gone.


    1. Pick the right RamNode instance

    FreeIPA runs Java (Dogtag), a directory server, a KDC, and Apache on one box. Undersized instances install fine and then fall over under real load.

    Deployment sizevCPURAMDisk
    Absolute minimum, lab only24 GB20 GB
    Recommended production baseline2 to 48 GB40 GB
    Larger realm or AD trust4+16 GB80 GB+

    Do not attempt this on 2 GB. The Dogtag CA installation alone will exhaust it and ipa-server-install will fail partway through, leaving you to uninstall and start over.

    Order a Standard or Premium KVM plan with an AlmaLinux 10 or Rocky Linux 10 template. Build this on a clean instance. FreeIPA expects to own ports 80, 443, 389, 636, 88, and 464. If a web server, another LDAP, or a reverse proxy is already listening, the install will conflict.


    2. Plan the names before you install

    Write these down and do not change them later:

    ItemExampleRule
    Server FQDNipa.example.comMust be a real A record, not a CNAME. Permanent.
    Kerberos realmEXAMPLE.COMUppercase. Conventionally matches the domain. Permanent.
    IPA domainexample.comLowercase. Permanent.

    Use a subdomain you control and are not already serving other things from. Many admins deploy into ipa.corp.example.com with realm CORP.EXAMPLE.COM to keep the identity zone separate from the public website zone.

    Renaming a FreeIPA server or realm after the fact means building a new realm and migrating. There is no rename tool.


    3. Prepare the server

    shell
    dnf -y update
    reboot

    Set the hostname to the full FQDN:

    shell
    hostnamectl set-hostname ipa.example.com

    FreeIPA resolves its own name during install. Pin it in /etc/hosts with the public IP, not 127.0.0.1:

    shell
    echo "203.0.113.10 ipa.example.com ipa" >> /etc/hosts

    Create the public DNS records at your registrar or DNS provider:

    shell
    ipa.example.com.  A     203.0.113.10
    ipa.example.com.  AAAA  2001:db8::10

    Set the PTR record for the instance in the RamNode control panel so reverse lookup returns ipa.example.com. Kerberos is sensitive to name mismatches and a wrong PTR produces authentication failures that look like clock problems.

    Verify all three directions before continuing:

    shell
    dig +short ipa.example.com
    dig +short -x 203.0.113.10
    hostname -f

    Time must be correct. Kerberos rejects tickets outside a five-minute skew.

    shell
    dnf -y install chrony
    systemctl enable --now chronyd
    chronyc sources

    Keep SELinux enforcing. FreeIPA is developed and tested with SELinux on, and disabling it causes more problems than it solves.

    shell
    getenforce

    4. Decide on integrated DNS

    FreeIPA can install its own BIND instance with an LDAP-backed zone. It manages SRV records for the realm automatically, which makes client discovery painless. On a public VPS it also means running a nameserver on a public IP, and a misconfigured one becomes an open resolver used for DNS amplification attacks. RamNode will notice that traffic and so will you.

    Option A, integrated DNS with recursion locked down. Best if you want automatic SRV management and you are careful.

    Option B, external DNS. Skip --setup-dns and create the SRV records yourself at your existing DNS provider. Fewer moving parts, one more manual step per service.

    This guide uses Option A with recursion restricted. If you choose Option B, drop the --setup-dns flags and add the SRV records from section 6.


    5. Open the firewall

    shell
    firewall-cmd --permanent --add-service=freeipa-4
    firewall-cmd --permanent --add-service=freeipa-ldap
    firewall-cmd --permanent --add-service=freeipa-ldaps
    firewall-cmd --permanent --add-service=dns
    firewall-cmd --permanent --add-service=ntp
    firewall-cmd --reload
    firewall-cmd --list-all

    freeipa-4 covers Kerberos on 88 and 464 plus the HTTP and HTTPS ports. freeipa-ldap and freeipa-ldaps open 389 and 636. dns opens TCP and UDP 53 for the embedded BIND.

    If your clients are all in known networks, scope the LDAP and Kerberos ports to those sources instead of leaving them world-open:

    shell
    firewall-cmd --permanent --new-zone=idmclients
    firewall-cmd --permanent --zone=idmclients --add-source=198.51.100.0/24
    firewall-cmd --permanent --zone=idmclients --add-service=freeipa-4
    firewall-cmd --reload

    6. Install the server

    shell
    dnf -y install ipa-server ipa-server-dns
    rpm -q ipa-server ipa-server-dns

    Run the installer. This is the interactive form, which is the right choice the first time:

    shell
    ipa-server-install --setup-dns

    Answer as follows:

    • Configure integrated DNS: yes
    • Server host name: ipa.example.com
    • Domain name: example.com
    • Realm name: EXAMPLE.COM
    • Directory Manager password: strong, stored in your password manager, used for direct LDAP administration
    • IPA admin password: strong, this is the realm administrator
    • DNS forwarders: enter your preferred upstream resolvers, or answer no to use root hints
    • Reverse zone: decline unless you actually control the reverse zone for your IP block, which you do not on a shared VPS range

    The unattended equivalent, useful once you have done it and want it repeatable:

    shell
    ipa-server-install \
      --realm=EXAMPLE.COM \
      --domain=example.com \
      --hostname=ipa.example.com \
      --ds-password='DIRECTORY_MANAGER_PASSWORD' \
      --admin-password='IPA_ADMIN_PASSWORD' \
      --setup-dns \
      --forwarder=9.9.9.9 \
      --no-reverse \
      --no-ntp \
      --unattended

    Installation takes 10 to 25 minutes on a 2 vCPU instance. Do not interrupt it.

    Lock down the resolver immediately

    If you installed integrated DNS, restrict recursion to your own networks before you walk away:

    shell
    ipa dnsconfig-mod --allow-sync-ptr=false

    Then edit /etc/named/ipa-options-ext.conf:

    shell
    allow-recursion { localhost; 198.51.100.0/24; };
    allow-query-cache { localhost; 198.51.100.0/24; };
    rate-limit { responses-per-second 10; };
    shell
    systemctl restart named

    Test from an outside host that recursion is refused:

    shell
    dig @203.0.113.10 example.org

    You want REFUSED for anything outside your allowed ranges.

    If you skipped integrated DNS

    Create these SRV records in your external zone so clients can discover the realm:

    shell
    _kerberos._tcp.example.com.       SRV 0 100 88   ipa.example.com.
    _kerberos._udp.example.com.       SRV 0 100 88   ipa.example.com.
    _kerberos-master._tcp.example.com. SRV 0 100 88  ipa.example.com.
    _kerberos-master._udp.example.com. SRV 0 100 88  ipa.example.com.
    _kpasswd._tcp.example.com.        SRV 0 100 464  ipa.example.com.
    _kpasswd._udp.example.com.        SRV 0 100 464  ipa.example.com.
    _ldap._tcp.example.com.           SRV 0 100 389  ipa.example.com.
    _kerberos.example.com.            TXT "EXAMPLE.COM"

    7. First login and smoke test

    shell
    kinit admin
    klist
    ipa user-find admin
    ipa config-show

    If kinit fails, check clock skew and DNS first. Those are the cause roughly nine times out of ten.

    Open the web UI at https://ipa.example.com and log in as admin. The certificate is issued by the realm's internal CA, so your browser will warn until you either trust that CA or replace the web certificate (section 10).

    Run the health check:

    shell
    dnf -y install ipa-healthcheck
    ipa-healthcheck --failures-only

    8. Create users, groups, and policy

    shell
    ipa user-add anguyen --first=Alice --last=Nguyen --email=alice@example.com --password
    ipa group-add sysadmins --desc="Server administrators"
    ipa group-add-member sysadmins --users=anguyen

    Set a sane default shell and password policy:

    shell
    ipa config-mod --defaultshell=/bin/bash
    ipa pwpolicy-mod --minlength=12 --maxlife=365 --history=5 --minclasses=3
    ipa pwpolicy-add sysadmins --priority=1 --minlength=16 --maxlife=180

    Host-based access control decides who may log into which machine. FreeIPA ships an allow_all rule. Disable it once you have your own rules, not before:

    shell
    ipa hbacrule-add sysadmins_all --desc="Sysadmins to all hosts"
    ipa hbacrule-add-user sysadmins_all --groups=sysadmins
    ipa hbacrule-mod sysadmins_all --hostcat=all --servicecat=all
    ipa hbacrule-disable allow_all

    Test the rule before you rely on it:

    shell
    ipa hbactest --user=anguyen --host=web01.example.com --service=sshd

    Centralized sudo:

    shell
    ipa sudorule-add sysadmins_sudo
    ipa sudorule-add-user sysadmins_sudo --groups=sysadmins
    ipa sudorule-mod sysadmins_sudo --hostcat=all --cmdcat=all
    ipa sudorule-add-option sysadmins_sudo --sudooption='!authenticate'

    Enable two-factor for administrators:

    shell
    ipa user-mod anguyen --user-auth-type=otp --user-auth-type=password
    ipa otptoken-add --owner=anguyen --type=totp

    9. Enroll a client

    On another RamNode instance, EL family:

    shell
    dnf -y install ipa-client
    ipa-client-install --mkhomedir --enable-dns-updates

    Debian or Ubuntu clients:

    shell
    apt install -y freeipa-client
    ipa-client-install --mkhomedir --enable-dns-updates

    Point the client's resolver at the IPA server first, or pass --server=ipa.example.com --domain=example.com explicitly when discovery is not available.

    Verify:

    shell
    id anguyen
    kinit anguyen
    klist

    Ubuntu note. The client packages are fine, but recent Ubuntu releases default to sudo-rs, which does not support the sudoers plugin interface that SSSD uses. FreeIPA sudo rules silently do not apply. Check with sudo --version and switch back if needed:

    shell
    update-alternatives --set sudo /usr/bin/sudo.ws

    There is no freeipa-server package on Ubuntu at all, only client tooling. If you must host the server on a Debian or Ubuntu VPS, run the official freeipa/freeipa-server container image with a bind-mounted /data volume and --sysctl net.ipv6.conf.all.disable_ipv6=0, and accept that in-place upgrades only work within the same base OS major version.


    10. Replace the web certificate with Let's Encrypt

    The internal CA is correct for host and service certificates inside the realm. For the web UI, a publicly trusted certificate saves everyone a browser warning.

    Use DNS-01 validation, since FreeIPA owns port 80:

    shell
    dnf -y install certbot
    certbot certonly --manual --preferred-challenges dns -d ipa.example.com

    Install it into both Apache and the directory server:

    shell
    ipa-server-certinstall -w -d \
      /etc/letsencrypt/live/ipa.example.com/fullchain.pem \
      /etc/letsencrypt/live/ipa.example.com/privkey.pem
    systemctl restart httpd dirsrv@EXAMPLE-COM.service

    -w installs for the web server and -d for the directory server. Repeat this after every renewal, or script it in a certbot deploy hook. Keep the internal CA intact; it still issues everything else.


    11. Add a replica

    A single FreeIPA server is a single point of failure for every login in the realm. Once you depend on it, add a second instance, ideally in a different RamNode location.

    On the primary, authorize the new host:

    shell
    kinit admin
    ipa host-add ipa2.example.com --ip-address=198.51.100.20
    ipa hostgroup-add-member ipaservers --hosts=ipa2.example.com

    On the replica, after setting hostname, hosts file, DNS, and PTR the same way:

    shell
    dnf -y install ipa-server ipa-server-dns ipa-client
    ipa-client-install --domain=example.com --server=ipa.example.com --mkhomedir
    ipa-replica-install --setup-ca --setup-dns --forwarder=9.9.9.9 --no-reverse

    Verify replication:

    shell
    ipa-replica-manage list
    ipa-csreplica-manage list
    ipa topologysegment-find domain

    Both servers now accept writes and replicate to each other. Point clients at both by listing both in DNS SRV records, which integrated DNS handles automatically.


    12. Backups

    ipa-backup is the only supported backup method. File-level copies of the directory server are not restorable.

    shell
    ipa-backup
    ls -lh /var/lib/ipa/backup/

    Full backups require the services to be stopped briefly. Data-only backups run online:

    shell
    ipa-backup --data --online

    Automate it and ship the result off the VPS:

    shell
    cat > /etc/cron.daily/ipa-backup <<'EOF'
    #!/bin/bash
    set -e
    /usr/sbin/ipa-backup --data --online --quiet
    find /var/lib/ipa/backup -maxdepth 1 -type d -mtime +14 -exec rm -rf {} +
    rsync -az /var/lib/ipa/backup/ backup@backup.example.com:/srv/backups/ipa/
    EOF
    chmod +x /etc/cron.daily/ipa-backup

    Restore with ipa-restore /var/lib/ipa/backup/ipa-full-2026-08-31-03-00-00. Restores must go back to the same hostname and realm, which is another reason those names are permanent.

    Take a RamNode snapshot before every ipa-server-upgrade or major package update.


    13. Maintenance

    shell
    ipactl status
    ipa-healthcheck --failures-only
    dnf -y update && ipa-server-upgrade

    Watch certificate expiry. The internal CA certificates renew automatically through certmonger, but a server that was powered off past an expiry date needs manual intervention:

    shell
    getcert list | grep -E 'status|expires'

    Directory server logs live in /var/log/dirsrv/slapd-EXAMPLE-COM/, Kerberos in /var/log/krb5kdc.log, and the framework in /var/log/httpd/error_log plus /var/log/ipa/.


    14. Troubleshooting

    ipa-server-install fails during the CA step. Almost always insufficient RAM or a hostname that does not resolve. Run ipa-server-install --uninstall, fix the cause, and start clean.

    kinit returns clock skew too great. Fix chrony on whichever side is drifting.

    Clients enroll but id user fails. SSSD cannot reach LDAP. Check firewall scoping on 389 and 636 and read /var/log/sssd/sssd_example.com.log.

    Web UI returns a 500 after an update. Run ipa-server-upgrade, which completes migrations that a plain package update leaves pending.

    Locked out of the admin account. Reset it as root on the server with kadmin.local and change_password admin.

    DNS queries from strangers in your logs. Your recursion restriction is not applied. Fix allow-recursion and restart named immediately.


    Where to go next

    Add the replica before you add users. Then look at Vault for secret storage, RADIUS proxying for network device authentication, and an Active Directory trust if you have a Windows domain to interoperate with. If FreeIPA feels heavier than your environment needs, and you do not need Kerberos or an internal CA, Kanidm covers the OIDC and Unix login cases on a fraction of the resources.