Skip to content

Lab Server Setup

The lab server is a Raspberry Pi named engineering that serves a Munki software repository to the lab Macs at http://engineering.local. Each lab Mac checks that address about once an hour and installs whatever is listed for it. This page rebuilds the server from a blank SD card to that working state.

The repository contents (installers, catalogs, and manifests) are not kept only on the Pi. The authoritative copy lives on the admin Mac (see Lab Software Update), and the last step on this page copies it back. A dead Pi loses nothing but an afternoon.

What You Need

  • Raspberry Pi 4 or 5 with its power supply
  • microSD card, 32 GB or larger (a USB SSD is better for the long run; see step 10)
  • A Mac with Raspberry Pi Imager installed
  • Lab Wi-Fi credentials, or an Ethernet cable to the lab VLAN (preferred)
  • The admin Mac with ~/munki_repo on it, for the final step

How the Pieces Fit

Where What Job
Admin Mac ~/munki_repo and publish.sh The master copy; munkiimport adds software here
Pi (engineering) nginx serving /srv/munki_repo Hands files to the lab Macs over plain HTTP
Pi Avahi (Bonjour) Answers to engineering.local without any DNS setup
Lab Macs Munki client Fetch http://engineering.local, install what's listed

Munki needs nothing from a web server beyond answering GET requests for files, so nginx with a one-line root directive is the whole server.

Image the SD Card

Steps are numbered continuously through this page so any step can be referred to by its number.

  1. Open Raspberry Pi Imager on a Mac. Choose your Pi model, then for the operating system choose Raspberry Pi OS (other) ▸ Raspberry Pi OS Lite (64-bit). The server has no screen, so the desktop would only use space.

  2. Choose the SD card as the storage, click Next, and choose Edit Settings when asked about OS customisation. Fill in the General tab:

    Setting Value
    Hostname engineering
    Username pi
    Password choose one and record it where the other lab passwords live
    Configure wireless LAN the lab Wi-Fi SSID and password, country US (skip if wired)
    Locale time zone America/Phoenix, keyboard us

    On the Services tab, enable SSH with password authentication. Save and write the card.

  3. Insert the card, connect Ethernet if you have it, and power on. Give it two minutes for the first boot.

  4. From any Mac on the lab network, confirm it's reachable and log in.

    ping -c 3 engineering.local
    ssh pi@engineering.local
    

    Your terminal should look like this: a pi@engineering:~ $ prompt. If ping can't resolve the name, wait another minute and try again; if it never resolves, the Mac you're testing from isn't on the same network as the Pi.

mDNS Stays Inside One Network

engineering.local is a Bonjour name, and Bonjour does not cross between VLANs. The Pi and the lab Macs must be on the same VLAN. A Mac on the staff Wi-Fi will not see it, which is correct behavior, not a fault.

Install the Server Software

The remaining Pi steps run inside the SSH session from step 4.

  1. Update the OS and install nginx, rsync, and Avahi (Avahi is normally present already; asking for it is harmless).

    sudo apt update && sudo apt full-upgrade -y
    sudo apt install -y nginx rsync avahi-daemon
    
  2. Confirm the hostname took, since everything else depends on it.

    hostname
    

    It should print engineering. If it doesn't, run sudo hostnamectl set-hostname engineering, then sudo sed -i 's/127.0.1.1.*/127.0.1.1\tengineering/' /etc/hosts, and sudo systemctl restart avahi-daemon.

  3. Create the repository tree. It's owned by pi so rsync from the admin Mac can write to it without root, and readable by everyone so nginx can serve it.

    sudo mkdir -p /srv/munki_repo/{catalogs,manifests,pkgs,pkgsinfo,icons,client_resources}
    sudo chown -R pi:pi /srv/munki_repo
    chmod -R u=rwX,go=rX /srv/munki_repo
    
  4. Write the nginx site definition.

    sudo tee /etc/nginx/sites-available/munki > /dev/null <<'NGINX'
    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name engineering.local engineering;
    
        root /srv/munki_repo;
        access_log /var/log/nginx/munki.access.log;
        error_log  /var/log/nginx/munki.error.log;
    
        # Directory listings make the repo browsable while troubleshooting.
        autoindex on;
    
        # Large installers: let the kernel stream them rather than buffering.
        sendfile on;
        tcp_nopush on;
    
        location / {
            try_files $uri $uri/ =404;
        }
    }
    NGINX
    

    Catalogs, manifests, and pkginfo files have no file extension. nginx serves them as application/octet-stream, which Munki is happy with; no MIME configuration is needed.

  5. Enable the site in place of nginx's default page and start it.

    sudo rm -f /etc/nginx/sites-enabled/default
    sudo ln -sf /etc/nginx/sites-available/munki /etc/nginx/sites-enabled/munki
    sudo nginx -t && sudo systemctl reload nginx
    sudo systemctl enable nginx
    curl -I http://localhost/catalogs/
    

    Your terminal should look like this: nginx -t reports syntax is ok and test is successful, and curl returns HTTP/1.1 200 OK.

  6. Optional, recommended. If you have a USB SSD, use it for the repository instead of the SD card. SD cards wear out under the rewrites that every publish causes, and an SSD doesn't. Format it on the Pi, mount it at /srv/munki_repo, add it to /etc/fstab, and repeat step 7. Nothing else on this page changes.

Give It a Fixed Address

engineering.local works whatever IP address the Pi gets, so this section is insurance against a DHCP change stranding the clients, not a requirement.

  1. Record both hardware addresses and send them to IT with a request for a DHCP reservation. Asking for both now means the Pi can be moved from Wi-Fi to Ethernet later without a second request.

    ip -br link show wlan0 eth0
    
  2. Make sure the Wi-Fi address isn't being randomized, or the reservation will never match.

    nmcli -g 802-11-wireless.cloned-mac-address connection show "$(nmcli -g NAME connection show --active | head -1)"
    

    Blank or permanent is correct. If it prints random or stable, run sudo nmcli con mod "<connection name>" 802-11-wireless.cloned-mac-address permanent and reconnect.

  3. Wire the Pi when you can. A 3.8 GB Fusion installer over gigabit Ethernet reaches a lab Mac in under a minute; over 2.4 GHz Wi-Fi it's a long afternoon.

Restore the Repository

These steps run on the admin Mac. They assume it is already set up per Lab Software Update; if both machines died at once, set up the admin Mac first and come back here.

  1. Give the admin Mac passwordless SSH to the Pi, so publishing never prompts.

    ssh-copy-id pi@engineering.local
    ssh pi@engineering.local 'echo ok'
    

    The second command should print ok with no password prompt.

  2. Publish the repository. This copies everything in ~/munki_repo to the Pi and sets permissions nginx can read.

    ~/munki_repo/publish.sh
    

    Expect the first publish to take a while if it includes Fusion; the pkg is 3.8 GB.

  3. Verify from the outside, on any Mac on the lab VLAN.

    curl -s http://engineering.local/manifests/site_default
    curl -sI http://engineering.local/catalogs/production | head -1
    

    Your terminal should look like this: the manifest prints as a plist, and the catalog returns 200 OK. On any enrolled lab Mac, sudo managedsoftwareupdate --checkonly will now talk to the new server with no client-side changes, because the address hasn't changed.

Done

The server is back. Lab Macs pick it up on their next hourly check.

Keeping It Running

  • Updates. Every few months, ssh pi@engineering.local and run sudo apt update && sudo apt full-upgrade -y && sudo reboot. Pick a time when the lab is empty; the reboot takes about a minute.
  • Disk space. df -h /srv/munki_repo on the Pi. Pruning old versions happens on the admin Mac with repoclean (see Lab Software Update), and the next publish removes them from the Pi.
  • Logs. /var/log/nginx/munki.access.log shows every request a lab Mac made. sudo grep ' 404 ' /var/log/nginx/munki.access.log | tail is the first thing to look at when a client complains.
  • If it stops responding. Power-cycle it. There is no state on the Pi that isn't also on the admin Mac, so the worst case is this page from step 1.