Tiny Deathstars of Foulness

Web Services in Mac OS X, Mac OS X Server, Linux and most versions of Unix are provided by Apache, an Open Source project that much of the Internet owes its origins to. Apache owes its name to the fact that it’s “a patchy” service. These patches are often mods, or modules. Configuring web services is as easy in OS X Server, running on Yosemite and El Capitan, as it has ever been. To set up the default web portal, simply open the Server app, click on the Websites service and click on the ON button.

Screen Shot 2015-09-25 at 9.31.32 PM

After a time, the service will start. Once running, click on the View Server Website link at the bottom of the pane.

Screen Shot 2015-09-25 at 9.32.00 PM

Provided the stock OS X Server page loads, you are ready to use OS X Server as a web server.

Screen Shot 2015-09-25 at 9.32.27 PM

Before we setup custom sites, there are a few things you should know. The first is, the server is no longer really designed to remove the default website. So if you remove the site, your server will exhibit inconsistent behavior. Also, don’t remove the files that comprise the default site. Instead just add sites, which is covered next. Webmail is gone. You don’t have to spend a ton of time looking for it as it isn’t there. Also, Mountain Lion Server added web apps, which we’ll briefly review later in this article as well, as those continue in Mavericks Server, Yosemite Server, and ultimately in OS X Server 5 for El Capitan.  Finally, enabling PHP and Python on sites is done globally, so this setting applies to all sites hosted on the server.

Screen Shot 2015-09-25 at 9.38.10 PM

Now that we’ve got that out of the way, let’s add our first custom site. Do so by clicking on the plus sign. At the New Web Site pane, you’ll be prompted for a number of options. The most important is the name of the site, with other options including the following:

  • Domain Name: The name the site is accessible from. The default sites do not have this option as they are accessible from all names that resolve to the server.
  • IP Address: The IP address the site listens on. Any means the site is available from every IP address the server is configured to use. The default websites do not have this option as they are accessible from all addresses automatically
  • Port: By default, sites without SSL run on port 80 on all network interfaces, and sites with SSL run on port 443 on all network interfaces. Use the Port field to use custom ports (e.g., 8080). The default sites do not have this option as they are configured to use 80 and 443 for default and SSL-based communications respectively.
  • SSL Certificate: Loads a list of SSL certificates installed using Keychain or the SSL Certificate option in the Settings pane of the Server application
  • Store Site Files In: The directory that the files that comprise the website are stored in. These can be placed into the correct directory using file shares or copying using the Finder. Click on the drop-down menu and then select Other to browse to the directory files are stored in.
  • Who Can Access: By default Anyone (all users, including unauthenticated guests) can access the contents of sites. Clicking on Anyone and then Customize… brings up the “Restrict access to the following folders to a chosen group” screen, where you can choose web directories and then define groups of users who can access the contents.
  • Additional Domains: Click on the Edit… button to bring up a simple list of domain names the the site also responds for (e.g. in addition to, add
  • Redirects: Click on the Edit… button to bring up a list of redirects within the site. This allows configuring redirects to other sites. For example, use /en to load or /cn to load
  • Aliases: Click on the Edit… button to load a list of aliases. This allows configuring redirects to folders within the same server. For example, /en loads /Library/Server/Web/Data/Sites/Default
  • Index Files: Click on the Edit… button to bring up a list of pages that are loaded when a page isn’t directly indicated. For example, when visiting, load the wp.php page by default.
  • Advanced Options: The remaining options are available by clicking on the “Edit Advanced Settings…” button.

Screen Shot 2015-09-25 at 9.38.35 PM

The Advanced Option include the following:

  • Enable Server Side Includes: Allows administrators to configure leveraging includes in web files, so that pieces of code can be used across multiple pages in sites.
  • Allow overrides using .htaccess files: Using a .htaccess file allows administrators to define who is able to access a given directory, defining custom user names and passwords in the hidden .htaccess file. These aren’t usually required in an OS X Server web environment as local and directory-based accounts can be used for such operations. This setting enables using custom .htaccess files instead of relying on Apple’s stock web permissions.
  • Allow folder listing: Enables folder listings on directories of a site that don’t have an Index File (described in the non-Advanced settings earlier).
  • Allow CGI execution: Enables CGI scripts for the domain being configured.
  • Use custom error page: Allows administrators to define custom error pages, such as those annoying 404 error pages that load when a page can’t be found
  • Make these web apps available on this website: A somewhat advanced setting, loads items into the webapps array, which can be viewed using the following command:  sudo serveradmin settings web:definedWebApps

Once you’ve configured all the appropriate options, click on Done to save your changes. The site should then load. Sites are then listed in the list of Websites.

The Apache service is most easily managed from the Server app, but there are too many options in Apache to really be able to put into a holistic graphical interface. The easiest way to manage the Websites service in OS X Yosemite Server is using the serveradmin command. Apache administrators from other platforms will be tempted to use the apachectl command to restart the Websites service. Instead, use the serveradmin command to do so. To start the service:

sudo serveradmin start web

To stop the service(s):

sudo serveradmin stop web

And to see the status:

sudo serveradmin fullstatus web

Fullstatus returns the following information:

web:health = _empty_dictionary
web:readWriteSettingsVersion = 1
web:apacheVersion = “2.2”
web:servicePortsRestrictionInfo = _empty_array
web:startedTime = “2015-09-26 02:38:57 +0000”
web:apacheState = “RUNNING”
web:statusMessage = “”
web:ApacheMode = 2
web:servicePortsAreRestricted = “NO”
web:state = “RUNNING”
web:setStateVersion = 1

While the health option typically resembles kiosk computers in the Computer Science departments of most major universities, much of the rest of the output can be pretty helpful including the Apache version, whether the service is running, any restrictions on ports and the date/time stamp that the service was started.

To see all of the settings available to the serveradmin command, run it, followed by settings and then web, to indicate the Websites service:

sudo serveradmin settings web

The output is pretty verbose and can be considered in two sections, the first includes global settings across sites as well as the information for the default sites that should not be deleted:

web:defaultSite:documentRoot = “/Library/Server/Web/Data/Sites/Default”
web:defaultSite:serverName = “”
web:defaultSite:realms = _empty_dictionary
web:defaultSite:redirects = _empty_array
web:defaultSite:enableServerSideIncludes = no
web:defaultSite:networkAccesses = _empty_array
web:defaultSite:customLogPath = “"/var/log/apache2/access_log"”
web:defaultSite:webApps = _empty_array
web:defaultSite:sslCertificateIdentifier = “”
web:defaultSite:fullSiteRedirectToOtherSite = “https://%{SERVER_NAME}”
web:defaultSite:allowFolderListing = no
web:defaultSite:serverAliases = _empty_array
web:defaultSite:errorLogPath = “"/var/log/apache2/error_log"”
web:defaultSite:fileName = “/Library/Server/Web/Config/apache2/sites/0000_127.0.0.1_34580_.conf”
web:defaultSite:aliases = _empty_array
web:defaultSite:directoryIndexes:_array_index:0 = “index.html”
web:defaultSite:directoryIndexes:_array_index:1 = “index.php”
web:defaultSite:directoryIndexes:_array_index:2 = “default.html”
web:defaultSite:allowAllOverrides = no
web:defaultSite:identifier = “67127006”
web:defaultSite:port = 34580
web:defaultSite:allowCGIExecution = no
web:defaultSite:serverAddress = “”
web:defaultSite:requiresSSL = no
web:defaultSite:proxies = _empty_dictionary
web:defaultSite:errorDocuments = _empty_dictionary

The second section is per-site settings, with an array entry for each site:

web:customSites:_array_index:0:documentRoot = “/Library/Server/Web/Data/Sites/”
web:customSites:_array_index:0:serverName = “”
web:customSites:_array_index:0:realms = _empty_dictionary
web:customSites:_array_index:0:redirects = _empty_array
web:customSites:_array_index:0:enableServerSideIncludes = no
web:customSites:_array_index:0:networkAccesses = _empty_array
web:customSites:_array_index:0:customLogPath = “/var/log/apache2/access_log”
web:customSites:_array_index:0:webApps = _empty_array
web:customSites:_array_index:0:sslCertificateIdentifier = “”
web:customSites:_array_index:0:fullSiteRedirectToOtherSite = “”
web:customSites:_array_index:0:allowFolderListing = no
web:customSites:_array_index:0:serverAliases = _empty_array
web:customSites:_array_index:0:errorLogPath = “/var/log/apache2/error_log”
web:customSites:_array_index:0:fileName = “/Library/Server/Web/Config/apache2/sites/”
web:customSites:_array_index:0:aliases = _empty_array
web:customSites:_array_index:0:directoryIndexes:_array_index:0 = “index.html”
web:customSites:_array_index:0:directoryIndexes:_array_index:1 = “index.php”
web:customSites:_array_index:0:directoryIndexes:_array_index:2 = “default.html”
web:customSites:_array_index:0:allowAllOverrides = no
web:customSites:_array_index:0:identifier = “67127002”
web:customSites:_array_index:0:port = 34580
web:customSites:_array_index:0:allowCGIExecution = no
web:customSites:_array_index:0:serverAddress = “”
web:customSites:_array_index:0:requiresSSL = no
web:customSites:_array_index:0:proxies = _empty_dictionary
web:customSites:_array_index:0:errorDocuments = _empty_dictionary
web:dataLocation = “/Library/Server/Web/Data”

The next section (the largest by far) includes array entries for each defined web app. The following shows the entry for a Hello World Python app:

web:definedWebApps:_array_index:0:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:0:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_ACSServer.conf”
web:definedWebApps:_array_index:0:requiredModuleNames:_array_index:0 = “”
web:definedWebApps:_array_index:0:startCommand = “”
web:definedWebApps:_array_index:0:sslPolicy = 1
web:definedWebApps:_array_index:0:requiresSSL = no
web:definedWebApps:_array_index:0:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:0:launchKeys:_array_index:0 = “”
web:definedWebApps:_array_index:0:proxies:/AccountsConfigService/api/:path = “/AccountsConfigService/api/”
web:definedWebApps:_array_index:0:proxies:/AccountsConfigService/api/:urls:_array_index:0 = “http://localhost:31415/AccountsConfigService/api”
web:definedWebApps:_array_index:0:preflightCommand = “”
web:definedWebApps:_array_index:0:stopCommand = “”
web:definedWebApps:_array_index:0:name = “”
web:definedWebApps:_array_index:0:displayName = “”
web:definedWebApps:_array_index:1:requiredWebAppNames:_array_index:0 = “”
web:definedWebApps:_array_index:1:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_corecollaboration_webauth.conf”
web:definedWebApps:_array_index:1:requiredModuleNames:_array_index:0 = “proxy_module”
web:definedWebApps:_array_index:1:requiredModuleNames:_array_index:1 = “headers_module”
web:definedWebApps:_array_index:1:startCommand = “”
web:definedWebApps:_array_index:1:sslPolicy = 4
web:definedWebApps:_array_index:1:requiresSSL = no
web:definedWebApps:_array_index:1:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:1:launchKeys = _empty_array
web:definedWebApps:_array_index:1:proxies:/auth:path = “/auth”
web:definedWebApps:_array_index:1:proxies:/auth:urls:_array_index:0 = “http://localhost:4444/auth”
web:definedWebApps:_array_index:1:preflightCommand = “”
web:definedWebApps:_array_index:1:stopCommand = “”
web:definedWebApps:_array_index:1:name = “”
web:definedWebApps:_array_index:1:displayName = “”
web:definedWebApps:_array_index:2:requiredWebAppNames:_array_index:0 = “”
web:definedWebApps:_array_index:2:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_corecollaboration_webcalssl.conf”
web:definedWebApps:_array_index:2:requiredModuleNames:_array_index:0 = “proxy_module”
web:definedWebApps:_array_index:2:requiredModuleNames:_array_index:1 = “headers_module”
web:definedWebApps:_array_index:2:startCommand = “”
web:definedWebApps:_array_index:2:sslPolicy = 1
web:definedWebApps:_array_index:2:requiresSSL = no
web:definedWebApps:_array_index:2:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:2:launchKeys = _empty_array
web:definedWebApps:_array_index:2:proxies = _empty_dictionary
web:definedWebApps:_array_index:2:preflightCommand = “”
web:definedWebApps:_array_index:2:stopCommand = “”
web:definedWebApps:_array_index:2:name = “”
web:definedWebApps:_array_index:2:displayName = “”
web:definedWebApps:_array_index:3:requiredWebAppNames:_array_index:0 = “”
web:definedWebApps:_array_index:3:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_corecollaboration_changepassword.conf”
web:definedWebApps:_array_index:3:requiredModuleNames:_array_index:0 = “proxy_module”
web:definedWebApps:_array_index:3:requiredModuleNames:_array_index:1 = “headers_module”
web:definedWebApps:_array_index:3:startCommand = “”
web:definedWebApps:_array_index:3:sslPolicy = 4
web:definedWebApps:_array_index:3:requiresSSL = no
web:definedWebApps:_array_index:3:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:3:launchKeys = _empty_array
web:definedWebApps:_array_index:3:proxies:/changepassword:path = “/changepassword”
web:definedWebApps:_array_index:3:proxies:/changepassword:urls:_array_index:0 = “http://localhost:4444/changepassword”
web:definedWebApps:_array_index:3:preflightCommand = “”
web:definedWebApps:_array_index:3:stopCommand = “”
web:definedWebApps:_array_index:3:name = “”
web:definedWebApps:_array_index:3:displayName = “”
web:definedWebApps:_array_index:4:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:4:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_corecollaboration_shared.conf”
web:definedWebApps:_array_index:4:requiredModuleNames:_array_index:0 = “proxy_module”
web:definedWebApps:_array_index:4:requiredModuleNames:_array_index:1 = “xsendfile_module”
web:definedWebApps:_array_index:4:requiredModuleNames:_array_index:2 = “headers_module”
web:definedWebApps:_array_index:4:requiredModuleNames:_array_index:3 = “expires_module”
web:definedWebApps:_array_index:4:requiredModuleNames:_array_index:4 = “deflate_module”
web:definedWebApps:_array_index:4:startCommand = “”
web:definedWebApps:_array_index:4:sslPolicy = 0
web:definedWebApps:_array_index:4:requiresSSL = no
web:definedWebApps:_array_index:4:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:4:launchKeys:_array_index:0 = “”
web:definedWebApps:_array_index:4:launchKeys:_array_index:1 = “”
web:definedWebApps:_array_index:4:proxies:/collabdproxy:path = “/collabdproxy”
web:definedWebApps:_array_index:4:proxies:/collabdproxy:urls:_array_index:0 = “http://localhost:4444/svc”
web:definedWebApps:_array_index:4:proxies:/__collabd/streams/activity:path = “/__collabd/streams/activity”
web:definedWebApps:_array_index:4:proxies:/__collabd/streams/activity:urls:_array_index:0 = “http://localhost:4444/streams/activity”
web:definedWebApps:_array_index:4:preflightCommand = “”
web:definedWebApps:_array_index:4:stopCommand = “”
web:definedWebApps:_array_index:4:name = “”
web:definedWebApps:_array_index:4:displayName = “”
web:definedWebApps:_array_index:5:requiredWebAppNames:_array_index:0 = “”
web:definedWebApps:_array_index:5:includeFiles = _empty_array
web:definedWebApps:_array_index:5:requiredModuleNames = _empty_array
web:definedWebApps:_array_index:5:startCommand = “”
web:definedWebApps:_array_index:5:sslPolicy = 0
web:definedWebApps:_array_index:5:requiresSSL = no
web:definedWebApps:_array_index:5:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:5:launchKeys:_array_index:0 = “”
web:definedWebApps:_array_index:5:launchKeys:_array_index:1 = “”
web:definedWebApps:_array_index:5:proxies = _empty_dictionary
web:definedWebApps:_array_index:5:preflightCommand = “”
web:definedWebApps:_array_index:5:stopCommand = “”
web:definedWebApps:_array_index:5:name = “”
web:definedWebApps:_array_index:5:displayName = “”
web:definedWebApps:_array_index:6:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:6:includeFiles = _empty_array
web:definedWebApps:_array_index:6:requiredModuleNames:_array_index:0 = “php5_module”
web:definedWebApps:_array_index:6:startCommand = “”
web:definedWebApps:_array_index:6:sslPolicy = 0
web:definedWebApps:_array_index:6:requiresSSL = no
web:definedWebApps:_array_index:6:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:6:launchKeys = _empty_array
web:definedWebApps:_array_index:6:proxies = _empty_dictionary
web:definedWebApps:_array_index:6:preflightCommand = “”
web:definedWebApps:_array_index:6:stopCommand = “”
web:definedWebApps:_array_index:6:name = “”
web:definedWebApps:_array_index:6:displayName = “”
web:definedWebApps:_array_index:7:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:7:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_webdavsharing.conf”
web:definedWebApps:_array_index:7:requiredModuleNames:_array_index:0 = “rewrite_module”
web:definedWebApps:_array_index:7:requiredModuleNames:_array_index:1 = “bonjour_module”
web:definedWebApps:_array_index:7:startCommand = “”
web:definedWebApps:_array_index:7:sslPolicy = 0
web:definedWebApps:_array_index:7:requiresSSL = no
web:definedWebApps:_array_index:7:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:7:launchKeys = _empty_array
web:definedWebApps:_array_index:7:proxies = _empty_dictionary
web:definedWebApps:_array_index:7:preflightCommand = “”
web:definedWebApps:_array_index:7:stopCommand = “”
web:definedWebApps:_array_index:7:name = “”
web:definedWebApps:_array_index:7:displayName = “”
web:definedWebApps:_array_index:8:requiredWebAppNames:_array_index:0 = “”
web:definedWebApps:_array_index:8:requiredWebAppNames:_array_index:1 = “”
web:definedWebApps:_array_index:8:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_corecollaboration_wiki.conf”
web:definedWebApps:_array_index:8:requiredModuleNames:_array_index:0 = “proxy_module”
web:definedWebApps:_array_index:8:requiredModuleNames:_array_index:1 = “headers_module”
web:definedWebApps:_array_index:8:startCommand = “”
web:definedWebApps:_array_index:8:sslPolicy = 0
web:definedWebApps:_array_index:8:requiresSSL = no
web:definedWebApps:_array_index:8:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:8:launchKeys:_array_index:0 = “”
web:definedWebApps:_array_index:8:launchKeys:_array_index:1 = “”
web:definedWebApps:_array_index:8:proxies:/__collabd/preview:path = “/__collabd/preview”
web:definedWebApps:_array_index:8:proxies:/__collabd/preview:urls:_array_index:0 = “http://localhost:4444/preview”
web:definedWebApps:_array_index:8:proxies:/wiki/files/upload:path = “/wiki/files/upload”
web:definedWebApps:_array_index:8:proxies:/wiki/files/upload:urls:_array_index:0 = “http://localhost:4444/upload_file”
web:definedWebApps:_array_index:8:proxies:/wiki/files/download:path = “/wiki/files/download”
web:definedWebApps:_array_index:8:proxies:/wiki/files/download:urls:_array_index:0 = “http://localhost:4444/files”
web:definedWebApps:_array_index:8:proxies:/wiki/ipad:path = “/wiki/ipad”
web:definedWebApps:_array_index:8:proxies:/wiki/ipad:urls = _empty_array
web:definedWebApps:_array_index:8:proxies:/wiki:path = “/wiki”
web:definedWebApps:_array_index:8:proxies:/wiki:urls:_array_index:0 = “http://localhost:4444/app-context/wiki”
web:definedWebApps:_array_index:8:preflightCommand = “”
web:definedWebApps:_array_index:8:stopCommand = “”
web:definedWebApps:_array_index:8:name = “”
web:definedWebApps:_array_index:8:displayName = “”
web:definedWebApps:_array_index:9:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:9:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_wsgi.conf”
web:definedWebApps:_array_index:9:requiredModuleNames:_array_index:0 = “wsgi_module”
web:definedWebApps:_array_index:9:startCommand = “”
web:definedWebApps:_array_index:9:sslPolicy = 0
web:definedWebApps:_array_index:9:requiresSSL = no
web:definedWebApps:_array_index:9:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:9:launchKeys = _empty_array
web:definedWebApps:_array_index:9:proxies = _empty_dictionary
web:definedWebApps:_array_index:9:preflightCommand = “”
web:definedWebApps:_array_index:9:stopCommand = “”
web:definedWebApps:_array_index:9:name = “”
web:definedWebApps:_array_index:9:displayName = “Python "Hello World" app at /wsgi”
web:definedWebApps:_array_index:10:requiredWebAppNames = _empty_array
web:definedWebApps:_array_index:10:includeFiles:_array_index:0 = “/Library/Developer/XcodeServer/CurrentXcodeSymlink/Contents/Developer/usr/share/xcs/httpd_xcs.conf”
web:definedWebApps:_array_index:10:requiredModuleNames = _empty_array
web:definedWebApps:_array_index:10:startCommand = “”
web:definedWebApps:_array_index:10:sslPolicy = 4
web:definedWebApps:_array_index:10:requiresSSL = no
web:definedWebApps:_array_index:10:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:10:launchKeys = _empty_array
web:definedWebApps:_array_index:10:proxies = _empty_dictionary
web:definedWebApps:_array_index:10:preflightCommand = “”
web:definedWebApps:_array_index:10:stopCommand = “”
web:definedWebApps:_array_index:10:name = “”
web:definedWebApps:_array_index:10:displayName = “”
web:definedWebApps:_array_index:11:requiredWebAppNames:_array_index:0 = “com.example.webapp.myotherwebapp”
web:definedWebApps:_array_index:11:includeFiles:_array_index:0 = “/Library/Server/Web/Config/apache2/httpd_myinclude.conf”
web:definedWebApps:_array_index:11:requiredModuleNames:_array_index:0 = “mystuff_module”
web:definedWebApps:_array_index:11:startCommand = “/usr/local/bin/startmywebapp”
web:definedWebApps:_array_index:11:sslPolicy = 0
web:definedWebApps:_array_index:11:requiresSSL = no
web:definedWebApps:_array_index:11:requiredByWebAppNames = _empty_array
web:definedWebApps:_array_index:11:launchKeys:_array_index:0 = “com.example.mywebapp”
web:definedWebApps:_array_index:11:proxies:/mywebapp:path = “/mywebapp”
web:definedWebApps:_array_index:11:proxies:/mywebapp:urls:_array_index:0 = “http://localhost:3000”
web:definedWebApps:_array_index:11:proxies:/mywebapp:urls:_array_index:1 = “http://localhost:3001”
web:definedWebApps:_array_index:11:preflightCommand = “/usr/local/bin/preflightmywebapp”
web:definedWebApps:_array_index:11:stopCommand = “/usr/local/bin/stopmywebapp”
web:definedWebApps:_array_index:11:name = “com.example.mywebapp”
web:definedWebApps:_array_index:11:displayName = “MyWebApp”

The final section defines the settings used for the default sites as well as a couple of host based settings:

web:defaultSecureSite:documentRoot = “/Library/Server/Web/Data/Sites/Default”
web:defaultSecureSite:serverName = “”
web:defaultSecureSite:realms = _empty_dictionary
web:defaultSecureSite:redirects = _empty_array
web:defaultSecureSite:enableServerSideIncludes = no
web:defaultSecureSite:networkAccesses = _empty_array
web:defaultSecureSite:customLogPath = “"/var/log/apache2/access_log"”
web:defaultSecureSite:webApps = _empty_array
web:defaultSecureSite:sslCertificateIdentifier = “”
web:defaultSecureSite:fullSiteRedirectToOtherSite = “”
web:defaultSecureSite:allowFolderListing = no
web:defaultSecureSite:serverAliases = _empty_array
web:defaultSecureSite:errorLogPath = “"/var/log/apache2/error_log"”
web:defaultSecureSite:fileName = “/Library/Server/Web/Config/apache2/sites/0000_127.0.0.1_34543_.conf”
web:defaultSecureSite:aliases = _empty_array
web:defaultSecureSite:directoryIndexes:_array_index:0 = “index.html”
web:defaultSecureSite:directoryIndexes:_array_index:1 = “index.php”
web:defaultSecureSite:directoryIndexes:_array_index:2 = “default.html”
web:defaultSecureSite:allowAllOverrides = no
web:defaultSecureSite:identifier = “67127004”
web:defaultSecureSite:port = 34543
web:defaultSecureSite:allowCGIExecution = no
web:defaultSecureSite:serverAddress = “”
web:defaultSecureSite:requiresSSL = yes
web:defaultSecureSite:proxies = _empty_dictionary
web:defaultSecureSite:errorDocuments = _empty_dictionary
web:mainHost:keepAliveTimeout = 15.000000
web:mainHost:maxClients = “256”

Each site has its own configuration file defined in the array for each section. By default these are stored in the /Library/Server/Web/Config/apache2/sites directory, with /Library/Server/Web/Config/apache2/sites/ being the file for the custom site we created previously. As you can see, many of the options available in the Server app are also available in these files:

DocumentRoot "/Library/Server/Web/Data/Sites/"
DirectoryIndex index.html index.php /wiki/ default.html
CustomLog /var/log/apache2/access_log combinedvhost
ErrorLog /var/log/apache2/error_log
SSLEngine Off
SSLProtocol -ALL +SSLv3 +TLSv1
SSLProxyEngine On
SSLProxyProtocol -ALL +SSLv3 +TLSv1

Options All -Indexes -ExecCGI -Includes +MultiViews
AllowOverride None


Deny from all
ErrorDocument 403 /customerror/websitesoff403.html

The serveradmin command can also be used to run commands. For example, to reset the service to factory defaults, delete the configuration files for each site and then run the following command:

sudo serveradmin command web:command=restoreFactorySettings

The final tip I’m going to give in this article is when to make changes with each app. I strongly recommend making all of your changes in the Server app when possible. When it isn’t, use serveradmin and when you can’t make changes in serveradmin, only then alter the configuration files that come with the operating system by default. For example, in this article I look at overriding some ports for some virtual sites that might conflict with other sites on your systems. I also recommend keeping backups of all configuration files that are altered and a log of what was altered in each, in order to help piece the server back together should it become unconfigured miraculously when a softwareupdate -all is run next.

October 10th, 2015

Posted In: Mac OS X Server, Mac Security, Mass Deployment

Tags: , , , , , , ,

I’ve finished the initial draft of the OS X Server 5 Guide. It’s available at

Screen Shot 2015-09-26 at 11.32.54 PM

There are likely to be further articles, as there are some strange things with this build of Server.

October 10th, 2015

Posted In: Mac OS X Server

Tags: , ,

A wiki is a repository of dynamically created and managed content, or content created or edited by multiple users collaboratively. This article is about using the wiki service in OS X Server (the Apple Server app running on 10.10 and 10.11). I reference file services with WebDAV because it is a very nice integration piece that I think a lot of people will find pretty beneficial.

To get started with the Wiki service, first turn it on. This one isn’t heavily dependent on host names (other than being able to access the server from a browser) or directory services (other than being able to authenticate users, but local accounts are perfectly functional) and it doesn’t require the Websites service to be running as well. One should always have good working directory services and host names, still…

To enable the service, open the Server app and click on Wiki in the list of SERVICES in the List Pane.

Screen Shot 2015-09-25 at 9.57.06 PM

There are two configuration options. The first is to select who is able to create wikis. Use the “Wikis can be created by” drop-down list to select “all users” if anyone with an account on the server should be able to create a wiki or “only some users” to bring up the Wiki Creators screen.

Screen Shot 2015-09-25 at 9.57.51 PM

If only some users can create new wikis, use the plus sign (“+”) at the Wiki Creators screen to add users and/or groups to the list of users that can create wikis. Click on OK when all users and groups that can create wikis are added. In a school I would imagine that only teachers or IT staff would be able to create wikis. Once a wiki is created, pages inside the wiki can still be created by non-wiki creators.

The other option available is the handy dandy WebDAV interface to the wikis. When you enable this option, you can connect to a server from OS X or iOS via WebDAV and access files in each wikis document repository. To be clear, this option doesn’t provide access to the user documents, but does provide access to the wiki documents. We’re going to check the box for “Enable WebDAV access to Wiki files” and then click the ON button.

Once the service starts, click on the View Wiki link in the Wiki workspace in Server app.

Screen Shot 2015-09-25 at 10.00.51 PM

Here, click on the Log in button and enter a user with access to the server, preferably one who can create wikis.

Screen Shot 2015-09-25 at 10.01.43 PM

At the Wikis page, you will then see a list of all wikis you have access to. Note that the previous screen showed one wiki and now we see two. That’s because one of the wikis has permissions that allow “All unauthenticated users” access to the wiki, which we’ll describe shortly. The first thing most administrators will do is create a wiki. To do so, click on the plus sign (“+”) icon on the web page and at the resultant screen, click on New Wiki.

Screen Shot 2015-09-25 at 10.02.35 PM

At the “Create a new wiki” prompt, provide a name for the wiki and a brief description for it.

Screen Shot 2015-09-25 at 10.03.12 PM

Click on Continue.

Screen Shot 2015-09-25 at 10.03.53 PM

At the Set permissions screen, enter each user or group to provide access to edit and view wiki pages. Here, you’ll have the options for Read & Write (users can view and edit pages in the wiki), Read only (users can only view the contents of your pages) and No access (users have no access to the wiki). There is a group for All logged in users, which includes every user with access to the server and another for All unauthorized users, which includes guests to the server. Once you’ve given the appropriate permissions, click on Continue.
Note: You don’t have to get this perfect now as you can always edit these later.

Screen Shot 2015-09-25 at 10.04.23 PM

At the Set Appearance screen, you can choose an icon for the wiki (shown in the wiki list and when you open the wiki) as well as a color scheme for the wiki. Choose the appropriate appearance for your wiki (again, you can always change this later) and then click on the Create button.

Screen Shot 2015-09-25 at 10.04.53 PM

Once the setup is finished, you’ll see the Setup complete modal. Here, you can click on Go to Wiki button.

Screen Shot 2015-09-25 at 10.05.33 PM

Once you’ve created your first wiki, let’s edit it and customize the content. To do so, click on it from the list of available wikis. Click on the cog-wheel icon and then Wiki Settings… to bring up the Wiki Settings page.

Screen Shot 2015-09-25 at 10.05.59 PM

Here, you’ll see the previously entered name and description as well as options to enable Calendar (only available if Calendar Server is running on the server) and Blog, which enables  a blog service for the wiki (wiki administrators can post blog entries to the wiki). Click on Appearance.

Screen Shot 2015-09-25 at 10.06.26 PM

Here, you will have the previous two options as well as the ability to upload a banner (which should be 62 pixels high) and background for each wiki.

Screen Shot 2015-09-25 at 10.07.02 PM

Click on Permissions. Here, you’ll see the permissions previously configured as well as options to configure who can comment on articles (nobody disables comments completely) in the wiki and whether comments require approval (moderation).

Click on Save. Now, let’s edit the splash page. To do so, click the pencil icon in the top navigation bar.

Screen Shot 2015-09-25 at 10.07.45 PM

At the edit screen, the top nav bar is replaced by a WYSIWIG editor for managing the page. Here you can justify, link, insert media and of course edit the text you see on the screen. I recommend spending some time embedding links, inserting tables, making text look like you want it to and editing the content to reflect the purpose of the wiki. Click Save when you’re done. Click the pencil again to edit it, and let’s create a new wiki page. Keep in mind that link wikipedia, each page should be linked to from other pages in the order they should be read. Unlike most wikis, there’s actually an index page of all the articles, which can come in handy.

Screen Shot 2015-09-25 at 10.08.34 PM

From the edit page, to create a new page and link to it, enter some text (or lasso some) that you’ll use as the link to access the new page you’re creating. Then click on the arrow and select “New page.”

Note: Use Enter URL to link to an existing page or an external website, instead of creating a new page.

Screen Shot 2015-09-25 at 10.09.03 PM

At the New Page screen, provide a name for the new page (the lasso’d text automatically appears as the Page Title) and click on the Add button.

Screen Shot 2015-09-25 at 10.09.40 PM

Click Save and then click on the newly created link. You can now edit the new page the same way you edited the previous pages. Click on the disclosure triangles in the right sidebar to Comment on articles, link articles to related articles, tag articles and view editing history.

Screen Shot 2015-09-25 at 10.12.44 PM

Now for the fun part. Click on Documents. Here, you’ll see the pages you already created. Click on the plus sign and select the option to Upload File to the wiki.

Screen Shot 2015-09-25 at 10.13.48 PM

At the Upload File dialog, click on Choose File and then select a file to upload.

Screen Shot 2015-09-25 at 10.14.36 PM

Click Upload when selected.

Screen Shot 2015-09-25 at 10.15.35 PM

Then from the Finder of an OS X client, use the Go menu to select “Connect to Server”. Enter the name or IP of the server and then click on Connect.

Assuming you can access the server, you should then be prompted for a username and password. Enter it and click Connect. Eventually, the file(s) will display (it can take awhile according to your network speeds and how many files are in the directory). You can connect to this same screen through an iPad using a 3rd party WebDAV client or the build in options in Pages.

Managing wikis is as easy as its ever been, with the new options for appearance being a nice add-on. Active Directory integration is as easy as binding the server to Active Directory and using the accounts listed in Permissions of pages. Overall, the ability to edit, upload and view documents from the Wiki is a great new feature in OS X Yosemite Server, worthy of checking out if you haven’t already!

Now that iOS devices can edit wikis and many of the traditional word processing options are available in the wiki editor, consider what the Wiki can be. Could it replace text editing apps for iOS? Could the Wiki allow for more collaborative documents than a Word or other document editor? Could it keep from getting eaten like the rest of the homework? Could the comments in the Wiki be a good way for teachers to have students write responses to materials? Could the Wiki and the document management features allow your workers to access human resources documents and employee manuals? I know plenty a tech firm that use wikis to track information about the systems they manage.

Once you have all of this information, upgrading can seem downright scary. But fear not, there’s Carbon Copy Cloner. And once you’ve cloned, there’s wikiadmin. When doing an upgrade in place, the Wiki service is pretty straight forward to upgrade, but in many cases, due to aging hardware, wiki services are moving from an older computer to a newer computer. This can be done in one of two ways. The first is to “migrate” the data by copying the Collaboration folder onto the new system. The second is to “export” and “import” the data. I usually recommend doing a migrate where possible, so we’ll start with that method.

Note: Before getting started, make sure that the directory services side of things is good. If a user or group lookup for an object that owns, edits or has commented on a wiki fails then that wiki probably shouldn’t be migrated. Use the dscl or id commands to confirm that lookups are functioning as intended.

To migrate wikis from one server to another, first copy the Collaboration directory to the new server. In this example, the directory has been dropped onto the desktop of the currently logged in user. To migrate the data once copied, use the wikiadmin command, along with the migration option. The option requires the path to the Collaboration folder, defined with -r, as follows:

sudo wikiadmin migrate -r ~/Desktop/Collaboration

When moving wikis, you can take the opportunity to get rid of a few you don’t want (such as that test wiki from way back when). Or administrators may just choose to move a single wiki to a new server in order to split the load across multiple hosts. When doing so, use the same command as earlier, along with the name of each wiki that is being moved, along with the -g option. For example, if moving the Legal wiki:

sudo wikiadmin migrate -r ~/Desktop/Collaboration -g Legal

The second way of moving wikis around is to export and then import them. To do so, first export wikis on the old server, using the wikiadmin command along with the export option, which requires an –exportPath option and needs to be done, on a wiki-by-wiki basis. So to export that Legal wiki to a file called LegalWikiTMP on the desktop:

sudo wikiadmin export -g Legal --exportPath ~/Desktop/LegalWikiTMP

Next, copy the wiki to the new server and import it, using the import option along with –importPath to identify where the file being imported is located. Using the same location, the command would then be:

sudo wikiadmin import -g Legal --importPath ~/Desktop/LegalWikiTMP

Note: The ability to import a wiki also allows for an API of sorts, as you can programmatically create wikis from other sources. The ability to export also provides a way to move into another wiki tool if you happen to outgrow the options provided in OS X Server and need to move to something more robust.

There is another way to move wikis, using pg_dump, copying the data and then using pg_restore to import the data once you’ve created the tables.  This way is, in my opinion, the last resort if the standard wikiadmin commands aren’t working. In my experience, if I’m doing the migration this way then I’ve got other, bigger issues that I need to deal with as well.

These commands work best when the wiki service has been started so that the databases are fully built out. To start the wiki service from the command line, use the serveradmin command instead of the wikiadmin command. The serveradmin command is used with the start option and then wiki is used to indicate the wiki service, as follows:

sudo serveradmin start wiki

The service can also be stopped, swapping out the start option with a stop option:

sudo serveradmin stop wiki

In a few cases (this is the main reason I’m writing this article), the attachments to wikis don’t come over during a migration. To migrate the files that are used for QuickLook, downloading attachments, etc, use the serveradmin command to locate the directory that these objects are stored in:

sudo serveradmin settings wiki:FileDataPath

The output identifies the directory where these objects are stored. Placing the contents in the same relative path as they are to the output of the same command on the target server usually results in restoring them. Once moved, use the fixPermissions option to repair the permissions of any files from the source (if any changes to account IDs are encountered such as an export/import rather than an archive/restore in OD this can lead to odd issues:

sudo wikiadmin fixPermissions

Also use the rebuildSearchIndex option with the wikiadmin command to fix any indexing, once the permissions have been repaired:

sudo wikiadmin rebuildSearchIndex

And finally use resetQuicklooks to clear any cached Quicklook representations of objects that have been inserted into a wiki and might not display properly using Quicklook (you know you might need to do this if they look fine when downloaded but look bad with Quicklook even though QuickLook on the server can view the files just fine):

sudo wikiadmin resetQuicklooks

When done properly the migration can take awhile. Keep in mind that every tag, every article, every edit to every article and basically everything else is tracked inside the tables that you’re moving. While there might not be a ton of data in the Collaboration directory or in an export, all of the data needs to go to the right location. This can take a little time in environments that have a lot of articles, even if they’re really short articles…

October 9th, 2015

Posted In: Mac OS X Server

Tags: , , , , , ,

OS X running the Server app has a lot of scripts used for enabling services, setting states, changing hostnames and the like. Once upon a time there was a script for OS X Server called server setup. It was a beautiful but too simplistic kind of script. Today, much of that logic has been moved out into more granular scripts, kept in /Applications/, used by the server to perform all kinds of tasks. These scripts are, like a lot of other things in OS X Server. Some of these include the configuration of amavisd, docecot and alerts. These scripts can also be used for migrating services and data. Sometimes the scripts are in bash, sometimes ruby, sometimes perl and other times even python. And the scripts tend to change year over year/release over release. The easiest way to view logs is to use the Server app, clicking on Logs in the sidebar. The dropdown at the bottom of the screen provides quick access to service-based logs.

Screen Shot 2015-09-25 at 8.47.29 PM

One of the things that can can be useful about the scripts scattered throughout the Server app is to learn how the developers of OS X Server intend for certain tasks to occur. However, you can also use the Console app from /Applications/Utilities, as with any other Mac, to look at standard logs.

Screen Shot 2015-09-25 at 8.48.50 PM

Looking At Services

This is also where I learned that Apple had put an Open Directory backup script in /Applications/ (that still requires a password). But what I haven’t seen in all of these logs is bumping up the logging level for services before performing tasks, so that you can see a verbose output of what’s going on. To do this, it looks like we’re going service-by-service. So let’s look alphabetically, starting with Address Book:

sudo serveradmin settings addressbook:DefaultLogLevel = “warn”

This by defualt logs to /var/log/caldavd/error.log, which is built based on the following, which sets the base:

sudo serveradmin settings addressbook:LogRoot=/var/log/caldavd

And the following, which sets the file name in that directory:

sudo serveradmin settings addressbook:ErrorLogFile=error.log

You can change either by changing what comes after the = sign. Next is afp. This service logs output to two places. The first is with errors to the service, using /Library/Logs/AppleFileService/AppleFileServiceError.log, the path designated in the following:

sudo serveradmin settings afp:errorLogPath = “/Library/Logs/AppleFileService/AppleFileServiceError.log”

The second location logs activities (open file, delete file, etc) rather than errors and is /Library/Logs/AppleFileService/AppleFileServiceAccess.log, defined using:

sudo serveradmin settings afp:activityLogPath = “/Library/Logs/AppleFileService/AppleFileServiceAccess.log”

The activity log is disabled by default and enabled using the command:

sudo serveradmin settings afp:activityLog = yes

The events that trigger log entries are in the afp:loggingAttributes array and are all enabled by default. There are no further controls for the verbosity of the afp logs. The next service is calendar. Similar to address book, the caldav server uses DefaultLogLevel to set how much data gets placed into logs:

sudo serveradmin settings calendar:DefaultLogLevel = “warn”

This by defualt logs to /var/log/caldavd/error.log, which is built based on the following, which sets the base:

sudo serveradmin settings calendar:LogRoot=/var/log/caldavd

And the following, which sets the file name in that directory:

sudo serveradmin settings calendar:ErrorLogFile=error.log

You can changing either by changing what comes after the = sign.
Profile Manager is called devicemgr in the serveradmin interface and I’ve found no way to augment the logging levels. Nor does its migration script ( /Applications/ ) point to any increased logging during migration.

The dirserv (aka Open Directory) uses the slapconfig back-end, so I use slapconfig to increase logging:

sudo slapconfig -enableslapdlog

The DNS service uses named.conf, located in /etc to set log levels and has no serveradmin settings for doing so. Here, use the logging section and look for both the file setting (by default /Library/Logs/named.log) for where the log is stored as well as the severity setting, which can set the logging levels higher or lower.

By default Messages, or iChat Server, logs a lot. See the following for what is logged:

sudo serveradmin settings jabber:logLevel = “ALL”

Adding the -D option to the LaunchDaemon that invokes jabber will increase the logs. Logging long-term is handled in each of the xml files that make up the features of jabber. See the Logconfiguration section of the c2s file via:

cat /Applications/

The mail service has a number of options for logging, much of which has to do with the fact that it’s a patchy solution made up of postfix, etc. Global log locations are controlled using the mail:global:service_data_path key, which indicates a path that logs are stored in (as usual many of these are in /Library/Server):

sudo serveradmin settings mail:global:service_data_path = "/Library/Server/Mail"

To see the virus database logging levels (which should usually be set to warn):

sudo serveradmin settings mail:postfix:virus_db_log_level

To see the spamassassin logging levels:

sudo serveradmin settings mail:postfix:spam_log_level

To see the actual postfix logging level:

sudo serveradmin settings mail:postfix:log_level

To enable timestamps on logs:

sudo serveradmin settings mail:imap:logtimestamps = yes

To set the dovecot logging to info:

sudo serveradmin settings mail:imap:log_level = “info”

To set increased logging per function that dovecot performs, see the config files in /Applications/, each of which has a logging section to do so.

The NetBoot service is simple to configure logging for, simply set the netboot:logging_level to HIGH (by default it’s MEDIUM):

sudo serveradmin settings netboot:logging_level = “HIGH”

The Postgres service uses a log directory, configured with postgres:log_directory:

sudo serveradmin settings postgres:log_directory = “/Library/Logs/PostgreSQL”

The /private/etc/raddb/radiusd.conf has a section (log {}) dedicated to configuring how the radius service logs output.

The Xsan service logs output per volume to both the System Log and volume-based log files, stored in /Library/Preferences/Xsan/data.

The smb service has a file /Library/Preferences/SystemConfiguration/ with a key for log level that can be used for more verbose output of the service.

The PPTP VPN service logs output to the file specified in vpn:Servers, configured with these:

sudo serveradmin settings = “/var/log/ppp/vpnd.log”
sudo serveradmin settings = “/var/log/ppp/vpnd.log”
sudo serveradmin settings = “/var/log/ppp/vpnd.log”
sudo serveradmin settings = “/var/log/ppp/vpnd.log”

By default, verbose logging is enabled, which you can see with:

sudo serveradmin settings
sudo serveradmin settings
sudo serveradmin settings
sudo serveradmin settings

The last service is web (Apache). The default access logs are per-site, with a key called customLogPath existing for each. The defaultSite uses the following for its logs:

sudo serveradmin settings web:defaultSite:customLogPath

Swap out the defaultSite with another site to see its log paths. There’s also a key for errorLogPath that shows errors. These are per-site so that administrators can provide access to logs for the owners of each site and not fear them having access to logs for other users. Global error logs are stored in /private/var/log/apache2/error_log as defined in /private/etc/apache2/httpd.conf. Find LogLevel in this file and set it to configure how in depth the logs will be, using debug for the most verbose and info, notice, warn, error, crit, alert, and emerg to get incrementally less information.

Additionally the log formats can be set in /private/etc/apache2/httpd.conf, allowing administrators to configure OS X  Server’s built-in web service to conform to the standards of most modern web log analyzers.


Overall, there’s a lot of information in these logs and administrators can spend as much time reviewing logs as they want. But other than standard system logs, the output is typically configured on a service-by-service basis. Some services offer a lot of options and others offering only a few. Some services also offer options within the serveradmin environment while others use their traditional locations in their configuration files. I’ll end this with a warning. There can also be a lot of output in these logs. Therefore, if you set the logging facilities high, make sure to keep a watchful eye on the capacity of the location you’re writing logs out to. The reason I looked at paths to logs where applicable was because you might want to consider redirecting logs to an external volume when debugging so as not to fill up a boot volume and cause even more problems than what you’re likely parsing through logs looking to fix…

October 8th, 2015

Posted In: Mac OS X Server, Mac Security, Mass Deployment

Tags: , , , , , , , ,

We covered using Time Machine Server previously. Here, we’re going to look at backing up the settings of an OS X Server. To do so, we’re going to install a little app called Bender from the great folks at Robot Cloud. You can download that from

Screen Shot 2015-09-24 at 10.51.00 PM

Once downloaded, run the package installer.

Screen Shot 2015-09-24 at 10.49.32 PM

At the Welcome to Bender screen, click Continue.

Screen Shot 2015-09-24 at 10.52.03 PM

Agree to the licensing agreement by clicking the Continue button.

Screen Shot 2015-09-24 at 10.52.22 PM

Click Agree again (assuming of course that you agree to the license).

Screen Shot 2015-09-24 at 10.52.57 PM

Choose who you wish to install the software for and click Continue.

Screen Shot 2015-09-24 at 10.53.52 PM

I’d go ahead and install Bender at the default location, so click Install. Once the installer is complete, you can view the installed LaunchDaemon at /Library/LaunchDaemons/net.robotcloud.bender.plist. Note that it calls the /usr/local/robotcloud/bin/bender binary when run at 22:00 every day. If you edit this file, the following settings are available:

macOS=$(sw_vers | awk ‘/ProductVersion/{print substr($2,1,5)}’ | tr -d “.”)
macSN=$(system_profiler SPHardwareDataType | awk ‘/Serial Number/{print $4}’)
date=$(date +%Y-%m-%d-%H%M)
pass=$(system_profiler SPHardwareDataType | awk ‘/Hardware UUID/{print $3}’)
pipTitle=”Bender Backup Error on: $macSN”

The most important of these is the backupDestination. You can set this to be the /Backups folder as it is above, or set it to be an external drive. Either should be backed up using your standard backup software.

October 8th, 2015

Posted In: Mac OS X, Mac OS X Server, Mac Security

Tags: , , , , , ,

The NetBoot service allows administrators of OS X computers to leverage images hosted on a server to boot computers to a central location and put a new image on them, upgrade them and perform automations based on upgrades and images. Since the very first versions of OS X, the service has been called NetBoot. In the Server app, Apple provides a number of options surrounding the NetInstall service, based on Automator-style actions, now calling the service NetInstall.

The first step to configuring the NetInstall service is to decide what you want the service to do. There are three options available in System Image Utility (available under the Tools menu of the Server app in OS X Server):

  • Create a NetBoot Image: Allows Macs to boot over the network to a disk image hosted on a server.
  • Create a NetInstall Image: Leverage NetBoot as a boot disk so that an image hosted on a server can be used to run an OS X installer.
  • Create a NetRestore Image: Leverage NetBoot as a boot disk so that you can restore a computer that has been configured over a network. Use this option to restore an image that has been prepared.

For the purposes of this example, we’re going to use an OS X El Capitan (10.11) installer running Server 5 to boot an OS X computer over the network. The first step in doing so is to create a Network Disk Image of 10.9, or the 10.9 installation media (which is the Install OS X Mavericks bundle for this example). Before setting it up, download the Install OS X El Capitan installer app into the /Applications directory from the App Store.

Create An Image

To then set up the NetBoot disk image (you can’t start the NetInstall service until you give it an image to serve), often referred to as the NetBoot set, open the Server app and then click on System Image Utility from the Tools menu of OS X.

Screen Shot 2015-09-26 at 8.25.36 PM
When System Image Utility opens, click on the Install OS X El Capitan entry in the list of available sources and click Next.

Screen Shot 2015-09-26 at 8.26.52 PM

Then, in the list of options, click on NetBoot Image and then click on the Next button.

Screen Shot 2015-09-26 at 8.27.40 PM

At the License Agreement screen, click Agree.

Screen Shot 2015-09-26 at 8.28.52 PM

Then provide an account name, short name and password in the Image Settings screen. Also choose the language of the user and select if you want the account to log in automatically. Once provided, click Next.

Screen Shot 2015-09-26 at 8.30.28 PM
Next, select any profiles, packages or post-install scripts to run on the NetBoot image once created. Here, you can use a profile to deploy a printer, bind to Active Directory, or use a package to install software. Post-install scripts allow you to do pretty much anything you’d like to a system, provided it’s allowed by SIP.

Screen Shot 2015-09-26 at 8.31.54 PM

At the System Configuration screen, choose how you’d like systems to receive names. Here, you can provide a name as a base for computers to get a computer name or you can use a file to deploy names. In most cases, you should also check the box for “Match to client after install.” Click Next once you’ve selected how this should occur.

Screen Shot 2015-09-26 at 8.33.21 PM

At the Directory Servers screen, click on the plus sign if you’d like to bind the system to a particular directory server.

Screen Shot 2015-09-26 at 8.33.33 PM

In this example, we’re binding to Also provide an account with access to bind to where you’re binding. In this case, we’re using the built-in admin account for Active Directory. Click Add once you’ve provided the appropriate directory server and credentials.

Screen Shot 2015-09-26 at 8.35.01 PM

At the Image Settings screen, provide a name for the image, as well as how the index number for the image is created. Note that each image should have a unique image index, so unless you’re storing your image on multiple servers, it’s best left at the defaults. Click Next.

Screen Shot 2015-09-26 at 8.36.53 PM

At the Supported Computer Models screen, you can choose which models of computer you don’t wish to support for this image. We’re not doing that here, but it’s useful, for example, if you’d like to preclude desktops from an image.

Screen Shot 2015-09-26 at 8.37.57 PM

At the Filter Clients By MAC Address, you can choose to explicitly allow or deny given MAC addresses for computers. We’re not going to do that as part of this workflow, so just click Next (unless of course you’d like to do that).

Screen Shot 2015-09-26 at 8.38.13 PM
Then, when prompted, select a location to store the Disk Image, provide any tags to be applied to the files that comprise the image and click on Save.

Screen Shot 2015-09-26 at 8.38.58 PM
The computer will then start creating the NetBoot set.


Setup The NetInstall Service

Once finished, it’s time to set up the NetInstall service in OS X Server. To get started, go back to the Server app.

Screen Shot 2015-09-26 at 8.39.33 PM
First, define which disk will host NetBoot Images. To do so, click on the Edit Storage Settings button. At the Storage Settings overlay, select the volume that Images will be hosted as well as the volume that Client Data will be hosted. The Image is what you are creating and the Client Data is dynamic data stored in images.

Screen Shot 2015-09-26 at 8.40.08 PM
If you only have one disk, as in this example, click on “Images & Client Data” for that disk. Then click on the OK button. Once you’ve selected a disk to store your image, we need to copy the disk image into the Library/NetBoot/NetBootSP0 folder of the disk used for images.

Screen Shot 2015-09-26 at 8.41.51 PM

Once in the appropriate folder, click on the Edit button for Network Interfaces and select the appropriate network interface you wish to serve images over, and click OK. Refresh the Server app (Command-R) and provided the image was created and moved into the /Library/NetBoot/NetBootSP0 directory of a volume set to host images, the image will appear in the images list, with a green indicator light.

Screen Shot 2015-09-26 at 9.04.37 PM

The green indicator light means the image is being served over the network. Double-click on an image.

Screen Shot 2015-09-26 at 9.04.41 PM
At the image settings screen, you can select NFS over the default HTTP protocol for “Make available over”.Note, you can also restrict access to the image to certain models of Apple computers and/or certain MAC addresses by using the “Image is visible to” and “Restrict access to this images” options respectively. Additionally, use the Make this image available for diskless booting option to allow computers without hard drives to boot to the image.

Screen Shot 2015-09-26 at 9.06.35 PM

Click on the OK button. Click on the image and then click on the cog-wheel icon. Click on “Use as Default Boot Image” to set an image to be the default images computers boot to when booting to NetBoot. Now, it’s as easy as clicking on the ON button. Do so to start the service.

Screen Shot 2015-09-26 at 9.07.10 PM
Once started, open a Terminal window. Here, let’s get a status of the service using the serveradmin fullstatus option (along with the service name, which is still netboot from the command line):

sudo serveradmin fullstatus netboot

The output of which shows the various components, logs and states of components:

netboot:state = "RUNNING"
netboot:stateTFTP = "RUNNING"
netboot:readWriteSettingsVersion = 1
netboot:netBootConnectionsArray = _empty_array
netboot:logPaths:netBootLog = "/var/log/system.log"
netboot:dhcpLeasesArray = _empty_array
netboot:stateDHCP = "STOPPED"
netboot:stateHTTP = "RUNNING"
netboot:serviceCanStart = 1
netboot:timeOfSnapshot = "2015-09-27 02:07:32 +0000"
netboot:stateNFS = "STOPPED"
netboot:stateImageArray:_array_index:0:_array_index:0 = 1
netboot:stateImageArray:_array_index:0:_array_index:1 = 0
netboot:stateImageArray:_array_index:0:_array_index:2 = 0
netboot:stateImageArray:_array_index:0:_array_index:3 = 1
netboot:stateImageArray:_array_index:0:_array_index:4 = 2
netboot:stateImageArray:_array_index:1:_array_index:0 = 0
netboot:stateImageArray:_array_index:1:_array_index:1 = 0
netboot:stateImageArray:_array_index:1:_array_index:2 = 0
netboot:stateImageArray:_array_index:1:_array_index:3 = 0
netboot:stateImageArray:_array_index:1:_array_index:4 = 2
netboot:stateImageArray:_array_index:2:_array_index:0 = 0
netboot:stateImageArray:_array_index:2:_array_index:1 = 0
netboot:stateImageArray:_array_index:2:_array_index:2 = 0
netboot:stateImageArray:_array_index:2:_array_index:3 = 0
netboot:stateImageArray:_array_index:2:_array_index:4 = 2
netboot:stateImageArray:_array_index:3:_array_index:0 = 0
netboot:stateImageArray:_array_index:3:_array_index:1 = 0
netboot:stateImageArray:_array_index:3:_array_index:2 = 0
netboot:stateImageArray:_array_index:3:_array_index:3 = 0
netboot:stateImageArray:_array_index:3:_array_index:4 = 2
netboot:servicePortsRestrictionInfo = _empty_array
netboot:netBootClientsArray = _empty_array
netboot:servicePortsAreRestricted = "NO"
netboot:setStateVersion = 1
netboot:startedTime = "2015-09-27 02:06:53 +0000"
netboot:stateAFP = "STOPPED"

And to start the service when not running:

sudo serveradmin start netboot

There are also a number of settings available at the command line that are not in the graphical interface. For example, to allow writing to the NetBoot share:

sudo serveradmin settings netboot:netBootStorageRecordsArray:_array_index:0:readOnlyShare = no

Or to get more verbose logs:

sudo serveradmin settings netboot:logging_level = "HIGH"

To stop the service:

sudo serveradmin stop netboot

In the beginning of this article, I mentioned that ways to configure NetInstall images. I’ll cover NetInstall and NetRestore in later articles as they tend to be more involved workflow-wise than copying a volume into a Network Disk Image. But to end this one, many an old-school admin might wonder where all the settings went that used to be in the GUI. Well, serveradmin still maintains a lot of the older stuff. To see a list of all available settings, run serveradmin with the settings verb and then netboot:

sudo serveradmin settings netboot

If there was a feature you want to use (e.g. maximum users), you should see it in the resultant list:

netboot:netBootFiltersRecordsArray = _empty_array
netboot:netBootStorageRecordsArray:_array_index:0:sharepoint = yes
netboot:netBootStorageRecordsArray:_array_index:0:clients = yes
netboot:netBootStorageRecordsArray:_array_index:0:volType = "hfs"
netboot:netBootStorageRecordsArray:_array_index:0:okToDeleteSharepoint = no
netboot:netBootStorageRecordsArray:_array_index:0:readOnlyShare = no
netboot:netBootStorageRecordsArray:_array_index:0:path = "/"
netboot:netBootStorageRecordsArray:_array_index:0:okToDeleteClients = yes
netboot:netBootStorageRecordsArray:_array_index:0:volName = "Macintosh HD"
netboot:netBootPortsRecordsArray:_array_index:0:deviceAtIndex = "en5"
netboot:netBootPortsRecordsArray:_array_index:0:nameAtIndex = "USB 10/100/1000 LAN"
netboot:netBootPortsRecordsArray:_array_index:0:isEnabledAtIndex = yes
netboot:logging_level = "MEDIUM"
netboot:filterEnabled = no
netboot:netBootImagesRecordsArray:_array_index:0:RootPath = "NetBoot.dmg"
netboot:netBootImagesRecordsArray:_array_index:0:IsInstall = no
netboot:netBootImagesRecordsArray:_array_index:0:Kind = "1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:0 = "MacBookAir6,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:1 = "MacBookAir5,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:2 = "MacBookAir7,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:3 = "MacBookAir2,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:4 = "MacBookAir5,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:5 = "MacBookAir4,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:6 = "MacBookAir4,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:7 = "MacBookAir6,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:8 = "MacBookAir7,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:9 = "MacBookAir3,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:10 = "MacBookAir3,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:11 = "MacBookPro5,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:12 = "MacBookPro9,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:13 = "MacBookPro6,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:14 = "MacBookPro6,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:15 = "MacBookPro8,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:16 = "MacBookPro11,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:17 = "MacBookPro7,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:18 = "MacBookPro11,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:19 = "MacBookPro10,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:20 = "MacBookPro12,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:21 = "MacBookPro11,4"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:22 = "MacBookPro11,5"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:23 = "MacBookPro3,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:24 = "MacBookPro4,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:25 = "MacBookPro8,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:26 = "MacBookPro10,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:27 = "MacBookPro5,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:28 = "MacBookPro5,5"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:29 = "MacBookPro5,4"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:30 = "MacBookPro5,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:31 = "MacBookPro9,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:32 = "MacBookPro11,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:33 = "MacBookPro8,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:34 = "iMac14,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:35 = "iMac9,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:36 = "iMac7,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:37 = "iMac12,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:38 = "iMac11,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:39 = "iMac14,4"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:40 = "iMac11,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:41 = "iMac13,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:42 = "iMac15,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:43 = "iMac12,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:44 = "iMac8,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:45 = "iMac10,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:46 = "iMac13,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:47 = "iMac14,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:48 = "iMac14,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:49 = "iMac13,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:50 = "iMac11,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:51 = "Macmini5,3"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:52 = "Macmini5,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:53 = "Macmini4,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:54 = "Macmini5,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:55 = "Macmini3,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:56 = "Macmini6,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:57 = "Macmini6,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:58 = "Macmini7,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:59 = "MacBook8,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:60 = "MacBook7,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:61 = "MacBook5,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:62 = "MacBook6,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:63 = "MacBook5,2"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:64 = "MacPro3,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:65 = "MacPro5,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:66 = "MacPro4,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:67 = "MacPro6,1"
netboot:netBootImagesRecordsArray:_array_index:0:DisabledSystemIdentifiers:_array_index:68 = "Xserve3,1"
netboot:netBootImagesRecordsArray:_array_index:0:Description = "NetBoot of OS X 10.11 (15A178w) Install (9.12 GB)."
netboot:netBootImagesRecordsArray:_array_index:0:Name = "NetBoot of Install OS X 10.11 El Capitan"
netboot:netBootImagesRecordsArray:_array_index:0:imageType = "netboot"
netboot:netBootImagesRecordsArray:_array_index:0:Index = 3089
netboot:netBootImagesRecordsArray:_array_index:0:osVersion = "10.11"
netboot:netBootImagesRecordsArray:_array_index:0:BackwardCompatible = no
netboot:netBootImagesRecordsArray:_array_index:0:SupportsDiskless = no
netboot:netBootImagesRecordsArray:_array_index:0:EnabledSystemIdentifiers = _empty_array
netboot:netBootImagesRecordsArray:_array_index:0:Language = "Default"
netboot:netBootImagesRecordsArray:_array_index:0:BootFile = "booter"
netboot:netBootImagesRecordsArray:_array_index:0:IsDefault = no
netboot:netBootImagesRecordsArray:_array_index:0:Type = "HTTP"
netboot:netBootImagesRecordsArray:_array_index:0:Architectures = "4"
netboot:netBootImagesRecordsArray:_array_index:0:IsEnabled = yes
netboot:netBootImagesRecordsArray:_array_index:0:pathToImage = "/Library/NetBoot/NetBootSP0/NetBoot of Install OS X 10.11 El Capitan.nbi/NBImageInfo.plist"
netboot:afpUsersMax = "50"

Boot to Your NetBoot Image

Next, you’ll want to have a computer boot to the NetBoot image you just created. Once upon a time, you would use the bless command to select a path to an image that you wanted to boot to in order to do so. Or you’d just boot holding down the N key and let the system pick an image. As of OS X 10.11, due to SIP restrictions, you’ll use the csrutil command to set a NetBoot address. To do so, run csrutil followed by the netboot option and then the add verb, followed by an address. In the following example, we’ll set the system to boot to the NetBoot server at

csrutil netboot add

Once you’ve finished any NetBoot workflows, use the remove verb to remove that address:

csrutil netboot remove

And to list any available NetBoot servers, use the list verb:

csrutil netboot list

Overall, all of this usually takes me a good 10 minutes of work, plus maybe up to half an hour of waiting for an image to create. You can use NetBoot to remotely boot systems, or NetInstall to remotely install systems. There are lots of articles out there (including here) on how to make sure clients can access these images over a network client, so I won’t rehash

October 7th, 2015

Posted In: Mac OS X, Mac OS X Server, Mac Security

Tags: , , , , , ,

OS X Server 5 is now available to be installed. To do so, first backup your server. Then, backup your server again, making sure you have a functional, bootable clone. Once you’re sure you have a solid backup of your server, open the App Store and search for Server. When you find the Server app, click on it.

Screen Shot 2015-09-23 at 10.23.26 PM

At the OS X Server screen, click on Install.

Screen Shot 2015-09-23 at 10.25.51 PM

The download will begin. Once complete, you’ll see a notice that the “Server app replacement detected.” Click OK. Then, open the Server app.

Screen Shot 2015-09-23 at 10.54.24 PM

When the Server app opens, you’ll be prompted to update the server. Click Continue.

Screen Shot 2015-09-23 at 10.58.30 PM

At the Licensing Agreement screen, click Agree.

Screen Shot 2015-09-23 at 10.59.08 PM

At the screen to confirm your administrative access, provide a name and password for an account with administrative access and then click on Allow.

Screen Shot 2015-09-23 at 11.00.26 PM

Services are then upgraded. Once complete, the Server app will open and should have settings consistent with the settings prior to the upgrade.

Screen Shot 2015-09-23 at 11.01.04 PM

September 26th, 2015

Posted In: Mac OS X Server

Tags: , , , , , ,