Debian

Set Up a Samba Active Directory Domain Controller on Debian 13

A Samba Active Directory domain controller lets a Debian 13 server do the job of a Windows Server DC. Windows 11 machines join it, users log on with Kerberos, and you manage it from the same RSAT consoles, so it suits a small office or a lab that needs AD without Windows Server licences.

Original content from computingforgeeks.com - post 180482

This guide provisions the domain with samba-tool, fixes the DNS traps the provision leaves behind, joins a Windows 11 client, manages users and Group Policy from RSAT, and reads the functional level Samba reports. We checked the build in October 2026 on Debian 13 with Samba 4.22 and on a Windows 11 24H2 client, and flagged the two steps we could not confirm.

How does a Samba AD DC fit with Windows 11?

Windows 11 finds the domain controller through DNS SRV records, so the Samba DC has to be the client’s DNS server. Samba runs its own DNS, a Heimdal Kerberos KDC, an LDAP directory and the SYSVOL share in one samba process, and it forwards every name outside the domain to an upstream resolver.

Samba AD DC architecture diagram: Windows 11 WIN11-LAB looks up SRV records, gets a Kerberos ticket, queries LDAP with ADUC and dsquery and pulls Group Policy from SYSVOL on dc01.ad.example.com, which forwards other names to 192.168.1.1

The numbers on the arrows are the order a Windows 11 logon follows. If step 1 fails, nothing after it works, which is why most Samba DC problems turn out to be DNS problems. It is also why the client should not list a public resolver next to the DC, because no public DNS server knows the SRV records for your private ad.example.com zone, so any lookup that lands there comes back empty.

Swap in your own values before you copy anything, since the lab values below appear in every command.

ItemLab valueWhat to use
DC hostnamedc01Short name, 15 characters or less
DC IP address192.168.1.231A static address set on the server itself
DNS domain and realmad.example.com / AD.EXAMPLE.COMA subdomain of a domain you own, never .local
NetBIOS domainEXAMPLEFirst label in capitals, 15 characters or less
Upstream DNS192.168.1.1Your router or another resolver
Windows 11 clientWIN11-LAB, 192.168.1.128Pro or Enterprise edition, which RSAT needs

What can’t a Samba DC do that a Windows DC can?

A Samba DC runs a domain at the Windows Server 2008 R2 functional level by default, and it can’t replicate SYSVOL to a second DC on its own. The day-to-day work a Windows 11 client does against a single DC (domain join, domain logon, Group Policy and DNS) worked in our lab. The limits only start to matter when you grow past one DC or need newer AD features:

  • The domain and forest stay at 2008 R2 unless you opt in to the partial 2012 to 2016 support that arrived in Samba 4.19.
  • SYSVOL is not replicated by DFS-R or FRS, so a second Samba DC needs an rsync job for Group Policy files.
  • Samba uses its bundled Heimdal KDC; MIT Kerberos support is still marked experimental.
  • Microsoft lists Windows 11 Pro or Enterprise as the requirement for RSAT, so the admin workstation can’t be a Home edition.

The Samba wiki’s DC setup page also recommends two or more DCs in production, for failover. With one DC, a dead server stops every password change, every logon that cannot fall back to cached credentials and every Group Policy refresh, so plan the second DC before the office depends on the first. If you need the newer levels, ADWS for the AD PowerShell module, or a vendor that only supports Microsoft DCs, a Windows Server AD DS install is the better fit. If your shop already runs Windows Server and only needs Linux machines to log in, you do not need any of this. In that case join Debian to the existing AD domain and keep the Windows DC.

What does the Debian 13 server need first?

The server needs a fixed IP, a short hostname that resolves to that IP, and a clock that stays in sync, because Kerberos rejects tickets once the clocks drift past five minutes. Our DC ran with 2 vCPUs and 4 GB of RAM, and the whole system used 596 MB of memory after provisioning, so 2 GB is a workable floor for a small office. Before you install anything, confirm these on the server:

  • The interface holds the static address the clients will use as their DNS server.
  • The hostname is not pdc, bdc or longer than 15 characters.
  • No other service, such as dnsmasq or BIND, listens on port 53.
  • The DNS domain is one you control, so it never collides with a public zone.

Set the hostname first. The provision writes it into the directory, the DNS zone and the Kerberos principals, so changing it later means far more than one command:

sudo hostnamectl set-hostname dc01

Map the full name to the LAN address in /etc/hosts. The Debian default points the hostname at 127.0.1.1, and a DC that resolves itself to loopback registers the wrong address in DNS. Delete the 127.0.1.1 line (on our server it was the only line carrying the hostname), then add the right one:

sudo sed -i '/127.0.1.1/d' /etc/hosts
echo "192.168.1.231 dc01.ad.example.com dc01" | sudo tee -a /etc/hosts

On a cloud image, cloud-init rewrites /etc/hosts at every boot and can also set the hostname back to the VM name. Turn both off so your edits survive a reboot:

echo "manage_etc_hosts: false" | sudo tee /etc/cloud/cloud.cfg.d/99-hosts.cfg
echo "preserve_hostname: true" | sudo tee /etc/cloud/cloud.cfg.d/99-hostname.cfg

Check that the server now knows its full name. If this prints only dc01, the hosts line is missing or sits below a loopback entry for the same name:

hostname -f

A correct setup prints the fully qualified name:

dc01.ad.example.com

With the name, address and clock settled, the server is ready for the Samba packages.

Which Samba packages does Debian 13 need for a DC?

Debian 13 ships the DC parts in their own samba-ad-dc package, on top of samba, winbind and the Kerberos client tools. If all you need is a shared folder with local accounts, stop here and follow the Samba file share guide for Debian instead, because a DC is far more to maintain than a share. We also add smbclient for the share test and chrony for time. The krb5-config package asks for a realm during install; the noninteractive frontend skips the questions, which is fine because the provision writes its own Kerberos file later:

sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt install -y acl attr samba samba-ad-dc winbind libpam-winbind libnss-winbind krb5-config krb5-user dnsutils python3-setproctitle smbclient chrony

The install starts the standalone file server straight away, which is the part most guides skip over. Debian enables and starts those daemons as soon as the packages land, while the DC unit waits for a config that does not exist yet. Check the four services:

systemctl is-active smbd nmbd winbind samba-ad-dc

On our server smbd, nmbd and winbind answered active, and samba-ad-dc answered inactive even though it was enabled:

active
active
active
inactive

A DC starts its own SMB and winbind processes, so the standalone ones must stop and stay stopped. We found smbd already listening on port 445 before the provision, and the DC needs that port for its own SMB server. Mask all three so nothing can start them again, then move the packaged configs out of the way, because the provision has to write a fresh smb.conf for the DC role.

sudo systemctl disable --now smbd nmbd winbind
sudo systemctl mask smbd nmbd winbind
sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.orig
sudo mv /etc/krb5.conf /etc/krb5.conf.orig
sudo systemctl enable --now chrony

Check the Samba version next, because the functional level options covered later only exist from 4.19 onward. You also want the exact build on record before the first update, so you can tell later whether a behaviour change came from Debian or from your own edits.

sudo samba --version

Our server reported the Debian 13 package build, and yours will show the same 4.22 series unless you added backports, with a higher deb13u number if Debian has shipped a security update since.

Version 4.22.11-Debian-4.22.11+dfsg-0+deb13u1

That is behind upstream, where the 4.25 series is current, and trixie-backports carries a 4.24 build. For a single DC we would stay on the Debian 13 package, because it gets Debian security fixes without moving the directory to a new major release under you. Backports make sense only when you need a fix or feature that 4.22 lacks, and then every DC in the domain should move together.

How do you provision the domain with samba-tool?

One samba-tool domain provision command creates the forest, the DNS zones, the Administrator account and the default Group Policy objects. We use the internal DNS server, which is what the Samba wiki recommends for new domains, and --use-rfc2307 so the directory can hold Unix UID and GID attributes later. Pick your own Administrator password; it must pass the domain complexity rules:

sudo samba-tool domain provision --server-role=dc --use-rfc2307 --dns-backend=SAMBA_INTERNAL --realm=AD.EXAMPLE.COM --domain=EXAMPLE --adminpass='Lab-Admin-2026!'

It finished in 14 seconds on our VM. The last lines summarise the domain, so check the hostname and both domain names before you go further. A typo here cannot be edited later, since Samba has no way to rename a realm, and the only fix is wiping /var/lib/samba and provisioning again.

INFO ... A Kerberos configuration suitable for Samba AD has been generated at /var/lib/samba/private/krb5.conf
INFO ... Merge the contents of this file with your system krb5.conf or replace it with this one. Do not create a symlink!
INFO ... Once the above files are installed, your Samba AD server will be ready to use
INFO ... Server Role:           active directory domain controller
INFO ... Hostname:              dc01
INFO ... NetBIOS Domain:        EXAMPLE
INFO ... DNS Domain:            ad.example.com
INFO ... DOMAIN SID:            S-1-5-21-533538618-2815108556-1420468854

Copy the generated Kerberos file into place, as the output says. It has to be a copy, not a symlink, because /var/lib/samba/private is readable by root only and tools like kinit run as your normal user.

sudo cp /var/lib/samba/private/krb5.conf /etc/krb5.conf

Then look at the smb.conf the provision wrote. It is short, and one line in it is wrong on any Debian 13 box that runs systemd-resolved.

sudo cat /etc/samba/smb.conf

The provision copied the forwarder from /etc/resolv.conf, which on our server was the resolved stub at 127.0.0.53. Everything else in the file is what a single DC needs.

# Global parameters
[global]
	dns forwarder = 127.0.0.53
	netbios name = DC01
	realm = AD.EXAMPLE.COM
	server role = active directory domain controller
	workgroup = EXAMPLE
	idmap_ldb:use rfc2307 = yes

[sysvol]
	path = /var/lib/samba/sysvol
	read only = No

[netlogon]
	path = /var/lib/samba/sysvol/ad.example.com/scripts
	read only = No

Leave the file as it is for now. The next section fixes the forwarder line along with the resolver it came from.

Why does DNS break right after the provision?

DNS breaks because the server still asks systemd-resolved for names, and resolved has never heard of ad.example.com. We started samba-ad-dc in that state to see what happens, and the service came up while its DNS updater flooded the journal:

/usr/sbin/samba_dnsupdate: ERROR(runtime): Record already exists; record could not be added. zone[ad.example.com] name[_ldap._tcp.Default-First-Site-Name._sites] [WERR_DNS_ERROR_RECORD_ALREADY_EXISTS] - (9711, 'WERR_DNS_ERROR_RECORD_ALREADY_EXISTS')
/usr/sbin/samba_dnsupdate: ERROR(runtime): Record already exists; record could not be added. zone[_msdcs.ad.example.com] name[gc] [WERR_DNS_ERROR_RECORD_ALREADY_EXISTS] - (9711, 'WERR_DNS_ERROR_RECORD_ALREADY_EXISTS')

The records were fine. samba_dnsupdate checks each one through the system resolver, gets no answer from resolved, tries to add it, and Samba’s own DNS replies that it already exists. The forwarder line has the opposite problem: once the DC becomes its own resolver, 127.0.0.53 points at a service you are about to stop, so every outside name would fail.

Check whether resolved runs on your server. Our Debian 13 cloud image ran it, with /etc/resolv.conf as a symlink to the stub file. If yours prints inactive, skip the disable step and only rewrite /etc/resolv.conf.

systemctl is-active systemd-resolved

If it prints active, stop and disable it, then replace the symlink with a static file that points at the DC itself. The DC must resolve its own domain through its own DNS server, or samba_dnsupdate keeps failing as shown above.

sudo systemctl disable --now systemd-resolved
sudo rm -f /etc/resolv.conf
printf "search ad.example.com\nnameserver 192.168.1.231\n" | sudo tee /etc/resolv.conf

Point the forwarder at a real upstream resolver. We used the LAN router at 192.168.1.1, and any resolver the server can reach works, including a public one, because only names outside ad.example.com ever go there.

sudo sed -i "s/dns forwarder = 127.0.0.53/dns forwarder = 192.168.1.1/" /etc/samba/smb.conf

Debian’s samba-ad-dc unit was already enabled on our server. Unmask it anyway in case an older guide had you mask it, then enable and restart it so it reads the new forwarder.

sudo systemctl unmask samba-ad-dc
sudo systemctl enable samba-ad-dc
sudo systemctl restart samba-ad-dc

After the restart the journal had no errors at all, and every AD port was listening. These are the ports a firewall between the clients and the DC must let through:

PortProtocolService
53TCP and UDPDNS
88TCP and UDPKerberos
135TCPRPC endpoint mapper
389TCP and UDPLDAP
445TCPSMB (SYSVOL, NETLOGON)
464TCP and UDPKerberos password change
636TCPLDAPS
3268, 3269TCPGlobal catalog
137, 138 / 139UDP / TCPNetBIOS name, datagram and session (nbt, smbd)
123UDPNTP from chrony, once set up below
49152-65535TCPDynamic RPC (rpc server dynamic port range)

Our Debian 13 VM had no host firewall rules, so nothing extra was needed. If you run nftables or another firewall on the DC, open those ports to the client subnet only. testparm -sv on our DC showed the default rpc server dynamic port range = 49152-65535, and ss showed the Samba nbt task on UDP 137 and 138 and smbd on TCP 139.

How do you check that DNS and Kerberos work?

Test from the DC itself before you touch a Windows machine, because a failed client join hides the real cause behind a generic error. Query the SRV records Windows will ask for, then one outside name to prove the forwarder works.

host -t SRV _ldap._tcp.ad.example.com.
host -t SRV _kerberos._udp.ad.example.com.
host -t A dc01.ad.example.com.
host -t A deb.debian.org

All four should answer. The SRV records name dc01 on ports 389 and 88, and deb.debian.org comes back through the forwarder. If the SRV queries fail but the outside name works, the server is still asking another resolver, so recheck /etc/resolv.conf.

_ldap._tcp.ad.example.com has SRV record 0 100 389 dc01.ad.example.com.
_kerberos._udp.ad.example.com has SRV record 0 100 88 dc01.ad.example.com.
dc01.ad.example.com has address 192.168.1.231
deb.debian.org is an alias for debian.map.fastlydns.net.
debian.map.fastlydns.net has address 151.101.66.132

Next, get a Kerberos ticket as the domain Administrator. Type the realm in capitals, because Kerberos realms are case sensitive and ad.example.com in lower case is a different realm that no KDC serves.

kinit [email protected]
klist

A ticket for krbtgt/AD.EXAMPLE.COM means the KDC works. Read the warning above it too, since it is easy to scroll past.

Warning: Your password will expire in 41 days on Fri Dec 11 08:14:19 2026
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: [email protected]

Valid starting     Expires            Service principal
10/30/26 08:15:12  10/30/26 18:15:12  krbtgt/[email protected]
	renew until 10/31/26 08:15:12

The new domain applies a 42-day maximum password age to everyone, including the Administrator you just created. On a lab or a single-admin office that becomes a lockout six weeks from now. Either put a reminder in your calendar or turn expiry off for that one account:

sudo samba-tool user setexpiry Administrator --noexpiry

Finish with the share and database checks. Windows clients read Group Policy and logon scripts from the two shares the provision created, so both must be listed, and the database check catches a broken provision before any client depends on it.

smbclient -L localhost -N
sudo samba-tool dbcheck --cross-ncs

The listing shows sysvol and netlogon, and our fresh domain held 3,841 objects with no errors. The SMB1 disabled line is expected and harmless, because Samba disables SMB1 by default and Windows 11 clients do not need it.

	Sharename       Type      Comment
	---------       ----      -------
	sysvol          Disk
	netlogon        Disk
	IPC$            IPC       IPC Service (Samba 4.22.11-Debian-4.22.11+dfsg-0+deb13u1)
SMB1 disabled -- no workgroup available
Checking 3841 objects
Checked 3841 objects (0 errors)

The provision creates no reverse zone, so an IP lookup of the DC goes to the forwarder and comes back empty. Add one for your subnet and a PTR record for the DC; samba-tool asks for the Administrator password on each command.

sudo samba-tool dns zonecreate dc01 1.168.192.in-addr.arpa -U Administrator
sudo samba-tool dns add dc01 1.168.192.in-addr.arpa 231 PTR dc01.ad.example.com -U Administrator
host 192.168.1.231

Our reverse lookup returned the DC’s full name:

231.1.168.192.in-addr.arpa domain name pointer dc01.ad.example.com.

With the zone in place, reverse lookups for the subnet are answered by the DC itself instead of going out to the forwarder.

Which functional level does a Samba AD DC report?

A fresh Samba 4.22 domain reports Windows Server 2008 R2 for the forest, the domain and the lowest DC. Read it with samba-tool:

sudo samba-tool domain level show

Our DC printed this, shown next to the service status and the Kerberos SRV record:

Samba AD DC on Debian 13 showing samba-ad-dc active, forest and domain functional level 2008 R2 and the Kerberos SRV record

Trying to raise the level straight away fails, and the error explains why. The DC itself advertises 2008 R2 as its highest level, so the domain can’t go above it:

sudo samba-tool domain level raise --domain-level=2016 --forest-level=2016

Samba refused with this message:

ERROR: Domain function level can't be higher than the lowest function level of a DC!

Samba 4.19 added partial support for the 2012, 2012 R2 and 2016 levels, and the 4.19 release notes describe the opt-in. The Samba wiki still says not to use it in production, so we tested it on the lab DC only. If you want 2008 R2, which is the right answer for most offices, skip to the next section.

You opt in with one smb.conf line under [global]. Open the file:

sudo vim /etc/samba/smb.conf

Add this line below server role, then save and restart samba-ad-dc:

	ad dc functional level = 2016

After the restart, samba-tool domain level show reported the lowest DC level as 2016 while the domain and forest stayed at 2008 R2. Then run the schema upgrade, the functional prep and the raise:

sudo systemctl restart samba-ad-dc
sudo samba-tool domain schemaupgrade --schema=2019
sudo samba-tool domain functionalprep --function-level=2016
sudo samba-tool domain level raise --domain-level=2016 --forest-level=2016

On a domain provisioned by Samba 4.22, the first two commands had nothing to do. The schema upgrade printed No changes applied to schema and the prep skipped every domain update, because the provision had already applied them. Only the raise changed anything:

Domain function level changed!
Forest function level changed!
All changes applied successfully!

A second level show reported 2016 on all three lines, and dbcheck passed again with 3,849 objects and 0 errors. Treat the raise as one-way: samba-tool domain level has only show and raise, and every DC you add later must also run with the 2016 opt-in, which rules out Samba releases older than 4.19.

How do you add users and groups from Debian?

You don’t need Windows for day-to-day accounts: samba-tool creates OUs, users and groups from the DC’s shell. We put a user in a Staff OU and a HelpDesk group, which the Windows side uses later:

sudo samba-tool ou create "OU=Staff,DC=ad,DC=example,DC=com"
sudo samba-tool user create jdoe 'Welcome-2026!' --given-name=Jane --surname=Doe [email protected] --userou=OU=Staff
sudo samba-tool group add HelpDesk
sudo samba-tool group addmembers HelpDesk jdoe

Check where the account landed and what it belongs to:

sudo samba-tool user show jdoe --attributes=distinguishedName,userPrincipalName,memberOf

Jane Doe sits in the Staff OU, with a UPN built from the realm:

dn: CN=Jane Doe,OU=Staff,DC=ad,DC=example,DC=com
userPrincipalName: [email protected]
memberOf: CN=HelpDesk,CN=Users,DC=ad,DC=example,DC=com
distinguishedName: CN=Jane Doe,OU=Staff,DC=ad,DC=example,DC=com

Note that the group went into the default Users container, because group add was not given an OU. Pass --groupou=OU=Staff if you want groups and users together.

How do you join Windows 11 to the Samba domain?

Point the client’s DNS at the DC, prove it can find the DC, then run Add-Computer. Our client was Windows 11 Enterprise 24H2, build 26100. Run these in an elevated PowerShell on the client, with the interface name from Get-NetAdapter. On a network where DHCP hands out the router as DNS, change the DHCP option instead, or every client needs this step by hand.

Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses 192.168.1.231
nltest /dsgetdc:ad.example.com

Our client found dc01 at once, and the flags list every role a Windows DC would advertise: PDC, global catalog, KDC, time server and writable. If nltest fails instead, stop here and fix DNS, because the join fails for the same reason with a less useful message.

           DC: \\dc01.ad.example.com
      Address: \\192.168.1.231
     Dom Guid: 308e5884-003a-4a3c-b686-4ff6639c7cdc
     Dom Name: ad.example.com
  Forest Name: ad.example.com
 Dc Site Name: Default-First-Site-Name
Our Site Name: Default-First-Site-Name
        Flags: PDC GC DS LDAP KDC TIMESERV GTIMESERV WRITABLE DNS_DC DNS_DOMAIN DNS_FOREST CLOSE_SITE FULL_SECRET
The command completed successfully

Give the PC its final name and reboot before this step, because the name it has at join time becomes its name in AD. Then join the domain with the Administrator account. PowerShell asks for the password and writes the computer account to the DC, then the client needs a restart:

Add-Computer -DomainName ad.example.com -Credential EXAMPLE\Administrator
Restart-Computer

Our template still had its factory hostname, so we added -NewName WIN11-LAB to rename and join in one command. The join worked, but it left a mismatch in AD: the computer account and its DNS name took the new name, while the object’s CN kept the old hostname, so ADUC and every LDAP path showed the old name. Renaming first, in its own step, avoids the combined path we took. If you already hit the mismatch, rename the object on the DC with ldbrename from the ldb-tools package, using the old name that samba-tool computer show WIN11-LAB prints on its dn: line. The tool edits sam.ldb directly, so take a backup first with sudo samba-tool domain backup offline --targetdir=/root/samba-backup, which wrote a samba-backup-*.tar.bz2 file on our DC.

sudo apt install -y ldb-tools
sudo ldbrename -H /var/lib/samba/private/sam.ldb "CN=OLD-NAME,CN=Computers,DC=ad,DC=example,DC=com" "CN=WIN11-LAB,CN=Computers,DC=ad,DC=example,DC=com"

Back on the DC, the new computer should be listed and carry the client’s OS details. These attributes are written by the client itself after the join, so an empty operatingSystem means the client has not rebooted yet.

sudo samba-tool computer show WIN11-LAB --attributes=distinguishedName,dNSHostName,operatingSystem,operatingSystemVersion

The client registered its OS build and full DNS name on its own, and it also created its own A record in the zone, which host win11-lab.ad.example.com on the DC confirmed.

dn: CN=WIN11-LAB,CN=Computers,DC=ad,DC=example,DC=com
operatingSystem: Windows 11 Enterprise Evaluation
operatingSystemVersion: 10.0 (26100)
dNSHostName: WIN11-LAB.ad.example.com
distinguishedName: CN=WIN11-LAB,CN=Computers,DC=ad,DC=example,DC=com

On the client, test the secure channel to the DC. Both checks below must pass before a domain user can log on:

Test-ComputerSecureChannel
nltest /sc_query:ad.example.com

Our client returned True and a successful trust to dc01:

True
Flags: 30 HAS_IP  HAS_TIMESERV  Authentication Service: Netlogon
Trusted DC Name \\dc01.ad.example.com
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

At this point the PC is a domain member, and a domain account can sign in at its logon screen, which we confirmed with the domain Administrator in the next section.

Do Windows 11 clients get their time from a Samba DC?

Not with Debian’s default chrony setup. Domain members ask the DC for signed time (the NT5DS mode), and chrony as installed above neither serves the LAN nor signs replies, so our client fell back to its own clock and logged event 24 every hour: “No valid response has been received from domain controller dc01.ad.example.com after 8 attempts to contact it.” That matters because Kerberos fails once the clocks drift five minutes apart.

The Samba wiki’s time sync page has chrony allow the client subnet and hand signing to Samba’s ntp_signd socket. Add both lines and give chrony’s group access to the socket directory:

printf "allow 192.168.1.0/24\nntpsigndsocket /var/lib/samba/ntp_signd\n" | sudo tee /etc/chrony/conf.d/samba-ad.conf
sudo chgrp _chrony /var/lib/samba/ntp_signd
sudo chmod 750 /var/lib/samba/ntp_signd
sudo systemctl restart chrony

Once restarted, chrony listened on UDP 123 and its journal printed “MS-SNTP authentication enabled”. The client could reach it too, since w32tm /stripchart /computer:dc01.ad.example.com returned samples with a 1.48 second offset. Check what Windows actually syncs from on the client:

w32tm /resync /rediscover
w32tm /query /source

Our client still answered Local CMOS Clock ten minutes after the change, even after a w32time restart, so we could not confirm a signed sync in this lab. On the DC, sudo chronyc clients listed the client, but sudo chronyc serverstats kept reporting Authenticated NTP packets : 0, so the client reached chrony while no signed reply went back. A healthy result names dc01.ad.example.com as the source on the client. If yours also stays on the CMOS clock, check journalctl -k on the DC for an AppArmor denial on the ntp_signd socket (chronyd runs confined on Debian 13, though ours logged none), and watch the offset with w32tm /stripchart before the drift reaches Kerberos range.

How do you manage Samba AD with RSAT?

The MMC consoles in RSAT work against a Samba DC, while the Active Directory PowerShell module does not. ADUC and GPMC talk LDAP and SMB, which Samba serves. The Get-AD* cmdlets go through Active Directory Web Services on port 9389, and Samba does not implement that service, so plan on the consoles plus samba-tool.

Install the AD and Group Policy tools on the Windows 11 client from an elevated prompt. They are Features on Demand, so the client downloads them from Windows Update:

dism /Online /Add-Capability /CapabilityName:Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
dism /Online /Add-Capability /CapabilityName:Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0

Budget real time for this. The AD tools took 74 minutes in the dism run that finished, because Windows Update pulled about a gigabyte of Features on Demand content and sat at 36 to 60 percent for long stretches; the Group Policy tools then took about 33 minutes. Add-WindowsCapability does the same job, but our first attempt with it failed with 0x80240438 and a second one hung, so we switched to dism, which prints its own progress bar. The Microsoft RSAT install page covers the other install routes and the Pro or Enterprise requirement.

We tried the AD module first once the tools landed, and it failed straight away:

Import-Module ActiveDirectory
Get-ADDomain

Every Get-AD* call returned the same error, even though Get-ADDomainController -Discover found dc01 through DNS:

WARNING: Error initializing default drive: 'Unable to find a default server with Active Directory Web Services running.'.
Get-ADDomain : Unable to find a default server with Active Directory Web Services running.
    + CategoryInfo          : ResourceUnavailable: (EXAMPLE:ADDomain) [Get-ADDomain], ADServerDownException
    + FullyQualifiedErrorId : ActiveDirectoryServer:1355,Microsoft.ActiveDirectory.Management.Commands.GetADDomain

The older LDAP command-line tools ship in the same RSAT package and work fine, which makes them the scripting option on the Windows side. Query the user created earlier and the domain’s functional level from cmd:

dsquery user -samid jdoe | dsget user -samid -upn -email -dn
dsquery user -samid jdoe | dsget user -memberof
dsquery * -filter (objectClass=domainDNS) -attr msDS-Behavior-Version

Windows sees the same user, the same groups and the raised level. A msDS-Behavior-Version of 7 is how AD stores the 2016 domain level; a domain left at 2008 R2 shows 4:

  dn                                              samid    upn                    email
  CN=Jane Doe,OU=Staff,DC=ad,DC=example,DC=com    jdoe     [email protected]    [email protected]
dsget succeeded
"CN=HelpDesk,CN=Users,DC=ad,DC=example,DC=com"
"CN=Domain Users,CN=Users,DC=ad,DC=example,DC=com"
  msDS-Behavior-Version
  7

For the consoles, sign in to the client as a domain account. We set the client to log on as EXAMPLE\Administrator, which also proved that an interactive domain logon against the Samba KDC works, then opened Active Directory Users and Computers (dsa.msc). It bound to dc01 without any extra setup and showed the directory built from Debian:

Active Directory Users and Computers on Windows 11 connected to the Samba AD DC, showing the ad.example.com domain, the Staff OU and the user Jane Doe
  1. 1ad.example.com
  2. 2Staff OU
  3. 3Jane Doe
  1. The ad.example.com domain node is the Samba directory, read over LDAP from dc01.
  2. The Staff OU is the one samba-tool ou create made on the DC.
  3. Jane Doe is the account samba-tool user create added, editable here like any AD user.

Group Policy Management (gpmc.msc) opened the same way. It listed the forest and domain, the Staff OU and the Group Policy Objects container, and its Status tab reported “dc01.ad.example.com is the baseline domain controller for this domain”:

Group Policy Management on Windows 11 managing the Samba AD domain ad.example.com with dc01 as the baseline domain controller

Each RSAT piece behaved differently against our Samba DC, so check this list before you plan your admin tooling around it:

  • ADUC opened the domain and showed the OU and user created with samba-tool.
  • GPMC and the GroupPolicy module listed the GPOs stored on the Samba DC and their links.
  • dsquery and dsget returned users, groups and the domain level.
  • Every Get-AD* cmdlet failed, because Samba has no Active Directory Web Services.

One terminal on the client sums it up: the secure channel to dc01, the GPO it applied, an LDAP query that works and the AD cmdlet that does not:

Windows 11 client in the Samba AD domain: nltest secure channel NERR_Success, gpresult applied from dc01, dsget user and Get-ADDomain Active Directory Web Services error

We did not install the DNS console on this client, because each Feature on Demand took over half an hour here. You can manage the zone from the DC with samba-tool dns as shown earlier, which needs nothing on Windows, so skip that capability unless your Windows admins insist on the GUI. If you also want a shared folder on one of the Windows 11 machines, the Windows 11 Samba share guide walks through it.

How do you push Group Policy from a Samba DC?

Windows 11 clients apply Group Policy from a Samba DC exactly as they do from a Windows DC, because the GPO is just an LDAP object plus files in SYSVOL. You can build one in GPMC, or entirely from Debian with samba-tool gpo, which is handy when you script the DC or have no Windows admin box yet. We built a machine policy that sets the logon notice, since its result is easy to see on the client.

The samba-tool gpo load command reads registry settings as JSON. Create the file on the DC:

vim ~/logon-notice.json

Add the two values Windows reads for the logon notice. Both are machine settings, so class is MACHINE:

[
    {
        "keyname": "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
        "valuename": "LegalNoticeCaption",
        "class": "MACHINE",
        "type": "REG_SZ",
        "data": "EXAMPLE domain"
    },
    {
        "keyname": "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
        "valuename": "LegalNoticeText",
        "class": "MACHINE",
        "type": "REG_SZ",
        "data": "This PC is managed by the Samba AD DC dc01.ad.example.com."
    }
]

Create an empty GPO first. samba-tool prints the GUID it assigned, and every later command refers to the GPO by that GUID, not by name, so copy it somewhere before you clear the terminal:

sudo samba-tool gpo create "Logon Notice" -U Administrator

Ours came back with this GUID:

GPO 'Logon Notice' created as {65BAA2E7-D265-4228-BAE9-BBD12BFEC5B5}

Load the JSON into it and link it to the domain root, so every computer in the domain picks it up. Linking to the root is right for a notice that should reach every PC, but you can link to an OU instead when the policy is for one group of machines. Replace the GUID with yours:

sudo samba-tool gpo load "{65BAA2E7-D265-4228-BAE9-BBD12BFEC5B5}" --content=$HOME/logon-notice.json -U Administrator
sudo samba-tool gpo setlink "DC=ad,DC=example,DC=com" "{65BAA2E7-D265-4228-BAE9-BBD12BFEC5B5}" -U Administrator

samba-tool gpo show with the same GUID reads the policy back from SYSVOL, with the registry client-side extension listed under Machine Exts and the version bumped to 1. On the Windows 11 client, force a computer policy refresh and ask which GPOs applied:

gpupdate /target:computer /force
gpresult /scope computer /r

Our client completed the update, and gpresult named dc01 as the source, with the new GPO in the applied list:

    Last time Group Policy was applied: 10/30/2026 at 2:51:55 PM
    Group Policy was applied from:      dc01.ad.example.com
    Domain Name:                        EXAMPLE
    Domain Type:                        Windows 2008 or later

    Applied Group Policy Objects
    -----------------------------
        Logon Notice

    The following GPOs were not applied because they were filtered out
    -------------------------------------------------------------------
        Default Domain Policy
            Filtering:  Not Applied (Empty)

        Local Group Policy
            Filtering:  Not Applied (Empty)

The registry on the client held both values right after the refresh. The Default Domain Policy shows as “Not Applied (Empty)” because a fresh Samba domain ships it with no settings, so Windows skips it until you add some. If your own GPO is missing from the list, check the link with samba-tool gpo getlink "DC=ad,DC=example,DC=com" before anything else.

At the next boot, Windows 11 showed the notice before the sign-in screen, with the text exactly as it sits in the JSON file on the Debian box:

Windows 11 logon notice EXAMPLE domain pushed by a Group Policy object created with samba-tool on a Samba AD DC

The same GPO is visible from the Windows side, which proves that GPMC and the GroupPolicy PowerShell module, unlike the AD module, work against Samba. Both read the GPO list and links through LDAP and SYSVOL:

Get-GPO -All -Domain ad.example.com | Select-Object DisplayName,GpoStatus,ModificationTime
(Get-GPInheritance -Target 'DC=ad,DC=example,DC=com' -Domain ad.example.com).GpoLinks | Select-Object DisplayName,Enabled,Order

All three GPOs came back, and the link order shows the new policy processed after the Default Domain Policy:

DisplayName                                GpoStatus ModificationTime
-----------                                --------- ----------------
Logon Notice                      AllSettingsEnabled 10/30/2026 2:51:46 PM
Default Domain Controllers Policy AllSettingsEnabled 10/30/2026 11:14:18 AM
Default Domain Policy             AllSettingsEnabled 10/30/2026 11:14:18 AM

DisplayName           Enabled Order
-----------           ------- -----
Default Domain Policy    True     1
Logon Notice             True     2

A logon notice blocks automatic logon, because someone has to press OK. We removed ours after the test with samba-tool gpo dellink and the same two arguments as setlink. Keep that in mind before you link a notice GPO to a domain that has kiosks or auto-logon machines.

Errors we hit and how we fixed them

Every message below came up during this build. Most of them point back at DNS or at a default that the provision chose for you, so read the cause before you reach for the fix.

samba_dnsupdate: “Record already exists; record could not be added”

The DC is resolving names through something other than itself, usually systemd-resolved on a cloud image. The updater cannot see the records it just created, so it tries to add them again and Samba’s DNS server refuses. Point /etc/resolv.conf at the DC’s own IP, fix the dns forwarder line, and restart samba-ad-dc. The journal stays clean after that.

“Domain function level can’t be higher than the lowest function level of a DC!”

samba-tool domain level raise prints this when the DC itself still advertises 2008 R2. Samba only advertises 2012 and above when ad dc functional level is set in smb.conf and the service has been restarted. Check the “Lowest function level of a DC” line in level show; once it reads 2016, the raise goes through.

“Warning: Your password will expire in 41 days”

Samba applies the default domain password policy (42 days) to the Administrator account the provision created. It is a warning today and a locked-out admin six weeks later. Run samba-tool user setexpiry Administrator --noexpiry, or change the domain policy with samba-tool domain passwordsettings set if you want the whole domain to behave differently.

“Unable to find a default server with Active Directory Web Services running”

Every Get-AD* cmdlet from the RSAT ActiveDirectory module fails this way against a Samba DC, because the module talks to Active Directory Web Services and Samba does not run that service. Nothing on the client or the DC fixes it. Use ADUC, GPMC, the GroupPolicy module, dsquery and dsget on Windows, and samba-tool on the DC for scripting.

“Add-WindowsCapability failed. Error code = 0x80240438”

Our first RSAT install died with this code within a minute, while the client had working internet and DNS through the DC. A retry then hung after the download, and a later dism run could not start until a reboot cleared the stuck servicing session. What finally worked was a reboot, then one dism /Add-Capability at a time, left alone until it printed “The operation completed successfully”. When a download sat with no traffic, setting the DODownloadMode Delivery Optimization policy to 0 (HTTP only) and restarting dosvc got bytes moving again in our lab, but treat that as a workaround, not a diagnosis.

Setup checklist

Run through this list after any rebuild or before you add more clients. Each item is a check you can do in under a minute, and the order follows the logon path in the diagram, so the first item that fails is the one to fix:

  1. hostname -f on the DC prints dc01.ad.example.com, not the short name.
  2. /etc/resolv.conf on the DC names only the DC’s own address.
  3. smb.conf has a dns forwarder that is a real resolver, not 127.0.0.53.
  4. smbd, nmbd and winbind are masked, and samba-ad-dc is active.
  5. host -t SRV _ldap._tcp.ad.example.com. returns the DC on port 389.
  6. kinit administrator gets a ticket without a clock skew error.
  7. samba-tool dbcheck --cross-ncs reports 0 errors.
  8. The Windows 11 client uses the DC as its only DNS server, and nltest /dsgetdc finds it.
  9. Test-ComputerSecureChannel on the client returns True.
  10. gpresult /r on the client lists your test GPO under applied objects.

Once all ten pass, the next job is a second DC for failover, and if you also run Linux servers, join Rocky Linux and AlmaLinux hosts to the same domain so one account works on both sides.

Keep reading

Create a Bootable Windows 11 USB and Install Windows 11 Windows Create a Bootable Windows 11 USB and Install Windows 11 Configure Samba File Share on Debian 13 / 12 Debian Configure Samba File Share on Debian 13 / 12 Backup and Restore Linux Systems with Timeshift Debian Backup and Restore Linux Systems with Timeshift Upgrade a Single Package on Ubuntu / Debian with apt Debian Upgrade a Single Package on Ubuntu / Debian with apt APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian Cheat Sheets APT Cheat Sheet: apt and apt-get Commands for Ubuntu/Debian How To Upgrade From Proxmox VE 7 To Proxmox VE 8 Debian How To Upgrade From Proxmox VE 7 To Proxmox VE 8

Leave a Comment

Press ESC to close