Autodiscover Records – Best Practices for Zimbra

Autodiscover Records – Best Practices for Zimbra

Deploying autodiscover records in public DNS simplifies adding a Zimbra account to any device. This blog post will show you how.

Introduction
Yes, we like the Zimbra web client very much (and are even starting to like the Modern UI too — though that’s another blog post…). But, sometimes you need to configure an IMAP, ActiveSync, CardDAV or CalDAV email client. Without autodiscover records, it’s a pain to add such accounts.  It forces users to enter a lot of information, and presents opportunities for users to make mistakes, causing frustration for the users and more tickets for the Help Desk.

With proper autodiscover records in public DNS, configuring those new IMAP, ActiveSync, CardDAV or CalDAV email clients is seamless. You’ll need to enter your username and password, and on iOS for example, choose “Manual Configuration”.  After doing so, you’ll be taken immediately to the Verify screen, which will immediately display successful verification checkmarks.

You are done! Easy-peasy!

Technical Background
Autodiscover records tell an email, calendar or address book client where to go to get its data.

Autodiscover records are deployed in the same email domain where the user’s email address lives. So if the user’s email address is like john.doe@mycompany.com, you’d need to deploy the autodiscover records in the public DNS zone file for the mycompany.com domain.

The FQDN in each autodiscover record doesn’t have to be on the same domain; that FQDN is typically the Zimbra Proxy server (or F5, AWS NLB or other Layer 4 load balancer) that john.doe@mycompany.com would normally connect to if using the Zimbra web client.

For our hosting environment, our Amazon Web Services Layer 4 Network Load Balancer has the FQDN “mail.missioncriticalemail.com” (and is also the PublicServiceHostname for all of the domains we host).

The DNS record examples below are all in BIND9 format. Do note the period at the end of the FQDN at the end of most of the records. In BIND9, this is required to prevent BIND9 from appending the domain to the entry. In other words, without the period at the end, BIND9 thinks the entry is a hostname, and appends the domain name of the domain where you are adding these records.  To be clear, if we were deploying the below autodiscover records on the mycompany.com domain, and didn’t include the period, the FQDN presented to the client would be “mail.missioncriticalemail.com.mycompany.com“.  No server lives there, so autodiscover would fail.

 

Implementation – IMAP
We need two autodiscover records for IMAP: one for the IMAP client to fetch email, and one for the IMAP client to send email via SMTP-Auth, using the Submission port TCP/587. Both are SRV records, and the records should look like those below. Note that it’s a good idea to start with a short TTL like we have done here (300 seconds), and then when you are comfortable that you got it right and that things are working OK, you should increase the TTL to like a day or so (86400 seconds for example).

_imaps._tcp      300 IN SRV 0 0 993 mail.missioncriticalemail.com.
_submission._tcp 300 IN SRV 0 0 587 mail.missioncriticalemail.com.

 

Implementation – ActiveSync
Here we also need two records, but one is a CNAME and one is an SRV. Same deal… start with a short TTL and then increase once you’ve tested and satisfied yourself that all is good.

autodiscover       300 IN CNAME       mail.missioncriticalemail.com.
_autodiscover._tcp 300 IN SRV 0 0 443 mail.missioncriticalemail.com.

 

Implementation – DAV
The DAV protocol is well-supported by Zimbra, and also by Apple products and almost all third-party email/calender/contacts clients with which I am familiar. If you are running Open Source Zimbra and don’t have ActiveSync, deploying IMAP, CardDAV and CalDAV on your workstation or mobile is a reliable way to go — especially if you have a large mailbox. (In our experience, ActiveSync is a bit fragile and doesn’t handle large mailboxes all that well all the time, especially on high-latency cellular connections).

For each of CardDAV and CalDav, you need two autodiscover records. The first as above tells the client where to find the server holding the user’s account. The second is to tell the client where on the server the user’s data lives within the user’s account:

_caldavs._tcp  300 IN SRV 0 0 443 mail.missioncriticalemail.com.
_caldavs._tcp  300 IN TXT "path=/dav/%u"
_carddavs._tcp 300 IN SRV 0 0 443 mail.missioncriticalemail.com.
_carddavs._tcp 300 IN TXT "path=/dav/%u"

 

Implementation – Thunderbird
Thunderbird is a special case, and it’s a bit more involved to configure autodiscovery for Thunderbird.  You’ll need one additional DNS record and an https web site to host an XML file. We use an AWS S3 bucket with an AWS Cloudfront Distribution as it costs about 10 cents per month, but if you have a web server someplace that will work just fine too.

The Thunderbird autodiscover DNS record should be something like some of the previous records:

autoconfig 300 IN CNAME drb4hreabfxdi.cloudfront.net.

In the case above, the CNAME points to the Cloudfront distribution, which is the actual webserver hosting the needed xml file (in two locations, for maximum compatibility).  Configuring a Cloudfront distribution is beyond the scope of this blog post, but we are happy to help customers do this if they wish.

The web site should have two directories: .well-known and mail.  The .well-known directory should have a subdirectory: mail.  In both the /mail and /.well-known/mail directories, you’ll need to deposit the same xml file, named config-v1.1.xml.  The file should be available, to use our case as an example, at like https://autoconfig.missioncriticalemail.com/mail/config-v1.1.xml.  The xml file contents look like what’s below — using our Zimbra hosting environment as an example — be sure to change the attributes to match your environment!

 

<?xml version="1.0" encoding="UTF-8"?>
<clientConfig version="1.1">
  <emailProvider id="missioncriticalemail.com">
    <domain>missioncriticalemail.com</domain>
    <displayName>Mission Critical Email</displayName>
    <displayShortName>MCE</displayShortName>

    <!-- IMAP Configuration -->
    <incomingServer type="imap">
      <hostname>mail.missioncriticalemail.com</hostname>
      <port>993</port>
      <socketType>SSL</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </incomingServer>

    <!-- SMTP Configuration -->
    <outgoingServer type="smtp">
      <hostname>mail.missioncriticalemail.com</hostname>
      <port>587</port>
      <socketType>STARTTLS</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </outgoingServer>

    <!-- Additional SMTP option (if you support port 465) -->
    <outgoingServer type="smtp">
      <hostname>mail.missioncriticalemail.com</hostname>
      <port>465</port>
      <socketType>SSL</socketType>
      <authentication>password-cleartext</authentication>
      <username>%EMAILADDRESS%</username>
    </outgoingServer>
  </emailProvider>
</clientConfig>

In our environment, we don’t allow port 465, so we actually didn’t include the last stanza.

It’s always good to “trust but verify”, so we downloaded the latest 142 version of Thunderbird for Mac (Arm). We added our username and password, and then Thunderbird let us select which address books and calendars to connect to:

 

DNS Validation Testing Script
OK, so autodiscover is really awesome — when things are working well — but how do you know if you are not a DNS expert that you got everything right before you let it all loose on your own customers or end users?

We wouldn’t leave you in the lurch!  Here’s a bash script you can use to test all of your autodiscover records against your own Zimbra system!  The script will ask for a username, the FQDN of your Zimbra server, and (optionally) the password for the user account.  It will then test if the Zimbra services are reachable by following the autodiscover records.

 

#!/bin/bash
# Copyright 2025 Mission Critical Email, LLC - All rights reserved.
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Arrays to track test results
declare -a PASSED_TESTS
declare -a FAILED_TESTS
declare -a NEEDS_INVESTIGATION

# Function to add test result
add_test_result() {
local test_name="$1"
local status="$2"

case $status in
"PASS") PASSED_TESTS+=("$test_name") ;;
"FAIL") FAILED_TESTS+=("$test_name") ;;
"INVESTIGATE") NEEDS_INVESTIGATION+=("$test_name") ;;
esac
}

# Prompt user for email address
echo -e "${BLUE}Zimbra Autodiscover DNS Configuration Validator${NC}"
echo "============================================="
echo -n "Enter email address (name@domain): "
read EMAIL

# Validate email format
if [[ ! "$EMAIL" =~ ^[^@]+@[^@]+\.[^@]+$ ]]; then
echo -e "${RED}Error: Invalid email address format. Please use format: name@domain${NC}"
exit 1
fi

# Extract username and domain
USERNAME=$(echo "$EMAIL" | cut -d'@' -f1)
DOMAIN=$(echo "$EMAIL" | cut -d'@' -f2)

# Ask for mail server hostname
echo -n "Enter mail server hostname (e.g. mail.domain.com, imap.domain.com): "
read MAIL_SERVER

# Validate mail server format
if [[ ! "$MAIL_SERVER" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo -e "${RED}Error: Invalid mail server hostname format${NC}"
exit 1
fi

# Ask for password (optional)
echo -n "Enter password for $EMAIL (press Enter to skip authenticated validation): "
read -s PASSWORD
echo

echo ""
echo -e "${BLUE}Validating autodiscover DNS configuration for $DOMAIN${NC}"
echo "Username: $USERNAME"
echo "Domain: $DOMAIN"
echo "Mail Server: $MAIL_SERVER"
echo "============================================="

echo -e "${BLUE}1. DNS SRV Records:${NC}"
echo "IMAPS:"
IMAPS_RESULT=$(dig SRV _imaps._tcp.$DOMAIN +short)
echo "$IMAPS_RESULT"
if [ ! -z "$IMAPS_RESULT" ]; then
add_test_result "DNS SRV _imaps._tcp" "PASS"
else
add_test_result "DNS SRV _imaps._tcp" "FAIL"
fi

echo "SUBMISSION:"
SUBMISSION_RESULT=$(dig SRV _submission._tcp.$DOMAIN +short)
echo "$SUBMISSION_RESULT"
if [ ! -z "$SUBMISSION_RESULT" ]; then
add_test_result "DNS SRV _submission._tcp" "PASS"
else
add_test_result "DNS SRV _submission._tcp" "FAIL"
fi

echo "AUTODISCOVER:"
AUTODISCOVER_RESULT=$(dig SRV _autodiscover._tcp.$DOMAIN +short)
echo "$AUTODISCOVER_RESULT"
if [ ! -z "$AUTODISCOVER_RESULT" ]; then
add_test_result "DNS SRV _autodiscover._tcp" "PASS"
else
add_test_result "DNS SRV _autodiscover._tcp" "FAIL"
fi

echo "CalDAV:"
CALDAV_RESULT=$(dig SRV _caldavs._tcp.$DOMAIN +short)
echo "$CALDAV_RESULT"
if [ ! -z "$CALDAV_RESULT" ]; then
add_test_result "DNS SRV _caldavs._tcp" "PASS"
else
add_test_result "DNS SRV _caldavs._tcp" "FAIL"
fi

echo "CardDAV:"
CARDDAV_RESULT=$(dig SRV _carddavs._tcp.$DOMAIN +short)
echo "$CARDDAV_RESULT"
if [ ! -z "$CARDDAV_RESULT" ]; then
add_test_result "DNS SRV _carddavs._tcp" "PASS"
else
add_test_result "DNS SRV _carddavs._tcp" "FAIL"
fi

echo -e "\n${BLUE}2. DNS TXT Records (DAV paths):${NC}"
echo "CalDAV path:"
CALDAV_TXT_RESULT=$(dig TXT _caldavs._tcp.$DOMAIN +short)
echo "$CALDAV_TXT_RESULT"
if [ ! -z "$CALDAV_TXT_RESULT" ]; then
add_test_result "DNS TXT _caldavs._tcp" "PASS"
else
add_test_result "DNS TXT _caldavs._tcp" "FAIL"
fi

echo "CardDAV path:"
CARDDAV_TXT_RESULT=$(dig TXT _carddavs._tcp.$DOMAIN +short)
echo "$CARDDAV_TXT_RESULT"
if [ ! -z "$CARDDAV_TXT_RESULT" ]; then
add_test_result "DNS TXT _carddavs._tcp" "PASS"
else
add_test_result "DNS TXT _carddavs._tcp" "FAIL"
fi

echo -e "\n${BLUE}3. DNS CNAME Records:${NC}"
echo "Autodiscover CNAME:"
CNAME_RESULT=$(dig CNAME autodiscover.$DOMAIN +short)
echo "$CNAME_RESULT"
if [ ! -z "$CNAME_RESULT" ]; then
add_test_result "DNS CNAME autodiscover" "PASS"
else
add_test_result "DNS CNAME autodiscover" "FAIL"
fi

echo -e "\n${BLUE}4. Autodiscover Service Availability:${NC}"
echo "ActiveSync/Exchange autodiscover endpoint:"
ACTIVESYNC_ENDPOINT=$(curl -I -s "https://autodiscover.$DOMAIN/autodiscover/autodiscover.xml" | head -1)
echo "$ACTIVESYNC_ENDPOINT"
if [[ "$ACTIVESYNC_ENDPOINT" =~ "200"|"401"|"405" ]]; then
add_test_result "ActiveSync Autodiscover Service" "PASS"
else
add_test_result "ActiveSync Autodiscover Service" "FAIL"
fi

echo "Thunderbird autoconfig endpoint:"
THUNDERBIRD_ENDPOINT=$(curl -I -s "https://autoconfig.$DOMAIN/mail/config-v1.1.xml" | head -1)
echo "$THUNDERBIRD_ENDPOINT"
if [[ "$THUNDERBIRD_ENDPOINT" =~ "200" ]]; then
add_test_result "Thunderbird Autoconfig Service" "PASS"
elif [[ "$THUNDERBIRD_ENDPOINT" =~ "404" ]]; then
add_test_result "Thunderbird Autoconfig Service" "FAIL"
else
add_test_result "Thunderbird Autoconfig Service" "INVESTIGATE"
fi

echo -e "\n${BLUE}5. Well-known URI Discovery:${NC}"
echo "CardDAV well-known URI:"
CARDDAV_WELLKNOWN=$(curl -I -s "https://$MAIL_SERVER/.well-known/carddav" | head -1)
echo "$CARDDAV_WELLKNOWN"
if [[ "$CARDDAV_WELLKNOWN" =~ "301"|"302"|"200" ]]; then
add_test_result "CardDAV Well-known URI" "PASS"
else
add_test_result "CardDAV Well-known URI" "FAIL"
fi

echo "CalDAV well-known URI:"
CALDAV_WELLKNOWN=$(curl -I -s "https://$MAIL_SERVER/.well-known/caldav" | head -1)
echo "$CALDAV_WELLKNOWN"
if [[ "$CALDAV_WELLKNOWN" =~ "301"|"302"|"200" ]]; then
add_test_result "CalDAV Well-known URI" "PASS"
else
add_test_result "CalDAV Well-known URI" "FAIL"
fi

echo -e "\n${BLUE}6. DAV Service Discovery:${NC}"
echo "DAV service endpoint availability:"
DAV_ENDPOINT=$(curl -I -s "https://$MAIL_SERVER/service/dav/home/" | head -1)
echo "$DAV_ENDPOINT"
if [[ "$DAV_ENDPOINT" =~ "200"|"401"|"405" ]]; then
add_test_result "DAV Service Discovery" "PASS"
else
add_test_result "DAV Service Discovery" "FAIL"
fi

# Optional authenticated validation
if [ ! -z "$PASSWORD" ]; then
echo -e "\n${BLUE}7. Authenticated Service Validation:${NC}"

echo "Testing ActiveSync autodiscover response format..."
ACTIVESYNC_AUTH_RESPONSE=$(curl -s -u "$EMAIL:$PASSWORD" -X POST "https://autodiscover.$DOMAIN/autodiscover/autodiscover.xml" \
-H "Content-Type: text/xml; charset=utf-8" \
-d "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006\">
<Request>
<EMailAddress>$EMAIL</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006</AcceptableResponseSchema>
</Request>
</Autodiscover>")

if [[ "$ACTIVESYNC_AUTH_RESPONSE" =~ "Autodiscover" ]] && [[ ! "$ACTIVESYNC_AUTH_RESPONSE" =~ "401" ]]; then
echo "ActiveSync response contains proper XML structure"
add_test_result "ActiveSync Response Format" "PASS"
elif [[ "$ACTIVESYNC_AUTH_RESPONSE" =~ "401" ]]; then
echo "ActiveSync authentication failed - check credentials"
add_test_result "ActiveSync Response Format" "FAIL"
else
echo "ActiveSync response format unexpected"
add_test_result "ActiveSync Response Format" "INVESTIGATE"
fi

echo "Testing CardDAV service response..."
CARDDAV_AUTH_RESPONSE=$(curl -s -u "$EMAIL:$PASSWORD" -X PROPFIND "https://$MAIL_SERVER/service/dav/home/" \
-H "Depth: 0" \
-H "Content-Type: text/xml" \
-d "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<propfind xmlns=\"DAV:\">
<prop>
<resourcetype/>
</prop>
</propfind>")

if [[ "$CARDDAV_AUTH_RESPONSE" =~ "multistatus" ]]; then
echo "CardDAV service responding with proper DAV XML"
add_test_result "CardDAV Service Response" "PASS"
elif [[ "$CARDDAV_AUTH_RESPONSE" =~ "401" ]]; then
echo "CardDAV authentication failed - check credentials"
add_test_result "CardDAV Service Response" "FAIL"
else
echo "CardDAV service response unexpected"
add_test_result "CardDAV Service Response" "INVESTIGATE"
fi

echo "Testing CalDAV service response..."
CALDAV_AUTH_RESPONSE=$(curl -s -u "$EMAIL:$PASSWORD" -X PROPFIND "https://$MAIL_SERVER/service/dav/home/" \
-H "Depth: 0" \
-H "Content-Type: text/xml" \
-d "<?xml version=\"1.0\" encoding=\"utf-8\"?>
<propfind xmlns=\"DAV:\">
<prop>
<resourcetype/>
</prop>
</propfind>")

if [[ "$CALDAV_AUTH_RESPONSE" =~ "multistatus" ]]; then
echo "CalDAV service responding with proper DAV XML"
add_test_result "CalDAV Service Response" "PASS"
elif [[ "$CALDAV_AUTH_RESPONSE" =~ "401" ]]; then
echo "CalDAV authentication failed - check credentials"
add_test_result "CalDAV Service Response" "FAIL"
else
echo "CalDAV service response unexpected"
add_test_result "CalDAV Service Response" "INVESTIGATE"
fi
fi

# Generate summary report
echo -e "\n${BLUE}=============================================${NC}"
echo -e "${BLUE}ZIMBRA AUTODISCOVER DNS VALIDATION REPORT${NC}"
echo -e "${BLUE}=============================================${NC}"

if [ ${#PASSED_TESTS[@]} -gt 0 ]; then
echo -e "\n${GREEN}✓ DNS CONFIGURATION VALIDATED (${#PASSED_TESTS[@]}):${NC}"
for test in "${PASSED_TESTS[@]}"; do
echo -e " ${GREEN}✓${NC} $test"
done
fi

if [ ${#NEEDS_INVESTIGATION[@]} -gt 0 ]; then
echo -e "\n${YELLOW}⚠ NEEDS REVIEW (${#NEEDS_INVESTIGATION[@]}):${NC}"
for test in "${NEEDS_INVESTIGATION[@]}"; do
echo -e " ${YELLOW}⚠${NC} $test"
done
echo -e " ${YELLOW}Note: These may be working but require manual verification${NC}"
fi

if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
echo -e "\n${RED}✗ DNS CONFIGURATION MISSING (${#FAILED_TESTS[@]}):${NC}"
for test in "${FAILED_TESTS[@]}"; do
echo -e " ${RED}✗${NC} $test"
done
fi

# Overall assessment
TOTAL_TESTS=$((${#PASSED_TESTS[@]} + ${#FAILED_TESTS[@]} + ${#NEEDS_INVESTIGATION[@]}))
CONFIGURED_ITEMS=$((${#PASSED_TESTS[@]} + ${#NEEDS_INVESTIGATION[@]}))

echo -e "\n${BLUE}DNS CONFIGURATION SUMMARY:${NC}"
echo "Total autodiscover components tested: $TOTAL_TESTS"
echo "Properly configured: ${#PASSED_TESTS[@]}"
echo "Need review: ${#NEEDS_INVESTIGATION[@]}"
echo "Missing configuration: ${#FAILED_TESTS[@]}"

if [ ${#FAILED_TESTS[@]} -eq 0 ]; then
echo -e "\n${GREEN}✓ AUTODISCOVER DNS CONFIGURATION COMPLETE${NC}"
echo -e "All required DNS records are properly configured for Zimbra autodiscovery."
elif [ ${#FAILED_TESTS[@]} -le 2 ]; then
echo -e "\n${YELLOW}⚠ AUTODISCOVER MOSTLY CONFIGURED${NC}"
echo -e "Core autodiscover functionality available, minor items need attention."
else
echo -e "\n${RED}✗ AUTODISCOVER CONFIGURATION INCOMPLETE${NC}"
echo -e "Multiple DNS records missing - clients may have difficulty with automatic setup."
fi

echo -e "\n${BLUE}Zimbra autodiscover DNS validation complete.${NC}"

 

The script will output to the console the results of its tests, and add a summary at the end. If all is good, the summary will look like this:

=============================================
ZIMBRA AUTODISCOVER DNS VALIDATION REPORT
=============================================

✓ DNS CONFIGURATION VALIDATED (16):
   ✓ DNS SRV _imaps._tcp
   ✓ DNS SRV _submission._tcp
   ✓ DNS SRV _autodiscover._tcp
   ✓ DNS SRV _caldavs._tcp
   ✓ DNS SRV _carddavs._tcp
   ✓ DNS TXT _caldavs._tcp
   ✓ DNS TXT _carddavs._tcp
   ✓ DNS CNAME autodiscover
   ✓ ActiveSync Autodiscover Service
   ✓ Thunderbird Autoconfig Service
   ✓ CardDAV Well-known URI
   ✓ CalDAV Well-known URI
   ✓ DAV Service Discovery
   ✓ ActiveSync Response Format
   ✓ CardDAV Service Response
   ✓ CalDAV Service Response

DNS CONFIGURATION SUMMARY:
Total autodiscover components tested: 16
Properly configured: 16
Need review: 0
Missing configuration: 0

✓ AUTODISCOVER DNS CONFIGURATION COMPLETE
All required DNS records are properly configured for Zimbra autodiscovery.

Zimbra autodiscover DNS validation complete.

 

 

Conclusions
Configuring autodiscover records simplifies account setups for all users, and will reduce your Help Desk tickets for sure (unless of course you make a mistake in one of the records and deploy the record with a long TTL…)!

It’s straightforward in every DNS system we’ve used to configure these records, and the benefits are immediate.

If you’d like help with your autodiscover or other email-related DNS records, fill out the form and we’ll get right back in touch:

Go back

Your message has been sent

Warning
Warning
Warning
Warning

Warning.

 

Hope that helps,
L. Mark Stone
Mission Critical Email LLC
22 July 2025
Updated 28 August 2025 to include a testing script and details on satisfying Thunderbird’s autodiscover needs

The information provided in this blog is intended for informational and educational purposes only. The views expressed herein are those of Mr. Stone personally. The contents of this site are not intended as advice for any purpose and are subject to change without notice. Mission Critical Email makes no warranties of any kind regarding the accuracy or completeness of any information on this site, and we make no representations regarding whether such information is up-to-date or applicable to any particular situation. All copyrights are reserved by Mr. Stone. Any portion of the material on this site may be used for personal or educational purposes provided appropriate attribution is given to Mr. Stone and this blog.