At the office, we had an older iMac 5K that had reached the end of its practical life as a macOS machine. Apple no longer supports the latest macOS releases on this model, which meant modern Xcode versions were unavailable and CI workloads that depended on recent SDKs became increasingly fragile. As a macOS device, it was effectively end of life.
From a hardware perspective, the story was very different.
This iMac still had a capable Intel CPU, plenty of RAM, fast SSD storage, and an excellent 5K display. Throwing that away felt wasteful. Instead, we decided to repurpose it as a secure, headless Debian-based status display: permanently on, fully automated, and controlled as infrastructure rather than a workstation.
This article is a practical how-to guide. It documents the full setup, the problems we ran into, and the troubleshooting steps we needed to make an iMac 5K behave like a reliable Linux appliance. It also highlights the security choices we made, because even a status screen deserves defensive thinking.
Back in 2019 we upgraded a Mac Mini to run Jenkins (full write-up here). This iMac eventually took over until Apple ended macOS support for the model. We now run macOS builds in Apple’s Xcode Cloud, and keep Jenkins on this iMac on Debian with current OS updates.
Note: this write-up is based on an iMac 5K 27-inch (Late 2014) with an AMD GPU. Other Intel-based iMac generations are similar, but the 5K panel behavior can differ slightly.
Hardware overview
The system used in this setup:
- iMac 5K 27-inch
- Intel CPU: i7, 8 cores
- 32 GB RAM
- AMD GPU
- SSD storage
- Integrated 5K Retina display
- Wired Ethernet
While macOS support stagnated, these specifications remain more than sufficient for Linux. In practice, this machine performs comfortably as an always-on dashboard system.
Practical note: the 5K panel is a special case. On some Linux setups it appears as two logical outputs. That can influence kiosk layout choices later in this guide.
Design goals and mindset
Before installing anything, we defined a few non-negotiables:
- Use Debian, not a desktop-oriented distribution
- No keyboard or mouse required after installation
- Chromium running full-screen in kiosk mode
- Treat it like infrastructure (not a personal workstation)
- No manual logins required
- Recover automatically on crashes
- Be controllable remotely
- Minimal attack surface
- Sustainable hardware reuse
This mindset drove almost every technical decision that followed.
Step 1: Installing Debian, minimal and explicit
We downloaded the Debian netinstaller, flashed it onto a thumb drive, and booted the iMac from it to perform a clean, non-graphical base install with proper disk formatting and swap.
Debian Trixie gives a modern base for a headless kiosk, with long-term security updates and predictable package behavior.
Key installation choices:
- EFI boot mode
- Non-free firmware enabled
- Minimal system utilities only
- No desktop environment
- No display manager
After installation, we explicitly verified EFI boot entries. Apple firmware sometimes makes surprising choices about boot order. Verifying this early prevents confusing boot failures later.
efibootmgr -vIf you are installing Debian 13 or newer, ensure your APT sources include non-free firmware. On Debian this is done by adding the component "non-free-firmware" to your repository lines.
deb http://deb.debian.org/debian trixie main contrib non-free-firmware
Step 2: Making the system truly headless
Although a display is attached, we intentionally avoided all desktop assumptions:
- No graphical login
- No local user interaction
- Everything started via systemd
We considered disabling getty on the primary console. In practice, keeping tty1 available can be useful for recovery. If you do disable it, ensure you have reliable SSH access and a fallback plan.
systemctl disable getty@tty1.serviceInstead of relying on interactive console access, we created a dedicated kiosk user and used systemd services and timers for automation.
Step 3: GPU firmware and the 5K display
The iMac 5K uses an AMD GPU paired with a very high DPI internal panel. We explicitly installed the required firmware. Without this, display behavior was inconsistent and sometimes unusable.
apt install firmware-amd-graphicsTo verify the kernel driver is bound correctly:
lspci -nnk | grep -A3 -E 'VGA|3D'If you hit a black screen after reboot, this is the first place to look. Firmware and output detection are usually the culprits.
Step 4: Installing Chromium for kiosk usage
We initially tested Google Chrome, but switched to Chromium for the final setup. Chromium integrates cleanly with Debian package management, avoids an external vendor repository, and fits better with a minimal, security-conscious, always-on system.
apt install chromiumWe launch Chromium in kiosk mode with a curated set of flags to remove distractions and reduce accidental exit paths.
chromium \
--kiosk \
--noerrdialogs \
--disable-infobars \
--no-first-run \
--disable-session-crashed-bubble \
--disable-features=TranslateUI \
--disable-pinch \
--overscroll-history-navigation=0 \
https://status.semonto.com/#display-mode
Step 5: Solving the double-screen issue
One of the most time-consuming problems was how the 5K panel presents itself to Linux. Internally, it may appear as multiple display outputs. Chromium then behaves as if two screens exist, which leads to incorrect spanning, off-center rendering, and broken full-screen behavior.
We solved this by running a minimal window manager (Openbox) and being explicit about the display layout. On this iMac, running Chromium directly under X without a WM could result in only half the panel being used.
If you prefer to solve it via xrandr, you can inspect the outputs and experiment with enabling a single combined mode. Auto-detection was unreliable on this hardware, so we treated display layout as a configuration problem, not a guess.
DISPLAY=:0 XAUTHORITY=/home/display/.Xauthority xrandrIf the dashboard appears split or misaligned, revisit the xrandr configuration. The internal 5K panel often reports multiple logical outputs.
Step 6: Supervising Chromium and handling crashes
Chromium is treated as unreliable by design. It is launched via a systemd service that starts on boot, automatically restarts on crash, and can be restarted remotely.
This is critical. The system must recover without human intervention. At this point, the iMac already behaved more like a digital signage appliance than a computer.
If Chromium does not start, check the systemd service logs and confirm the display environment variables are set correctly.
One subtle failure mode we hit is a race condition at boot: X may start before the 5K panel outputs are reported as connected. This can lead to X errors and a black screen.
A pragmatic fix is to delay X startup until the DRM connector reports a connected output (without relying on dmesg permissions):
# wait up to ~15s for any DRM output to become connected
for i in {1..15}; do
grep -q "connected" /sys/class/drm/*/status 2>/dev/null && break
sleep 1
done
Step 7: Hiding the mouse cursor
Even without a physical mouse attached, X will still display a cursor by default. On a wall-mounted dashboard, a visible cursor is distracting and makes the system look unfinished.
We installed unclutter to automatically hide the mouse cursor when it is not in use. This works reliably at the X level and survives Chromium restarts.
apt install unclutterunclutter is started alongside the display session and ensures the cursor disappears after a short idle period. Once enabled, the dashboard remains visually clean and kiosk-like.
We use an immediate hide to keep the display clean:
unclutter -idle 0 &Step 8: Sensors and hardware monitoring
Because this system is always on, we wanted visibility into hardware health.
apt install lm-sensors
sensors-detectThis provides insight into CPU temperature, thermal stability, and fan behavior over time.
Step 9: Fan control on iMac hardware
Apple hardware does not always expose fan control in a Linux-friendly way by default. On this iMac, fan speeds were too low under load, and the system ran warmer than expected.
We installed macfanctld to actively control fan speed based on temperature sensors.
apt install macfanctldOnce enabled, thermal behavior became predictable and long-term stability improved significantly.
If the system runs hot, verify macfanctld is running and reading the sensors correctly.
Step 10: Disabling sleep and power management
Debian defaults are optimized for desktops. For a wall-mounted kiosk we want the exact opposite: never suspend and never blank the display.
We disabled power management at multiple layers: systemd sleep targets, logind idle handling, and X-level screen blanking / DPMS.
First, we prevented suspend/hibernate at the OS level by masking the relevant systemd targets:
systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.targetSecond, we ensured systemd-logind does not trigger any idle actions or react to suspend keys. In /etc/systemd/logind.conf we set:
[Login]
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
IdleAction=ignoreThen we restarted logind to apply the changes:
systemctl restart systemd-logindFinally, we disabled X screen blanking and DPMS (display power management). These commands are executed when the kiosk display session starts:
xset s off
xset -dpms
xset s noblankIn practice, we discovered a fourth layer: Xorg and the GPU driver may re-enable DPMS timers even if you run xset. Symptoms include the display turning off after 10 minutes even though the system is still running.
To make DPMS behavior deterministic, we disabled blanking at the Xorg configuration level:
# /etc/X11/xorg.conf.d/10-dpms-off.conf
Section "ServerFlags"
Option "BlankTime" "0"
Option "StandbyTime" "0"
Option "SuspendTime" "0"
Option "OffTime" "0"
EndSection
Section "Monitor"
Identifier "iMacPanel"
Option "DPMS" "false"
EndSection
With these layers in place, the iMac stays awake and the 5K panel keeps showing the status page indefinitely.
Step 11: Local control API
To make the dashboard manageable without SSH access, we exposed a small local control API. This allows the system to be controlled and recovered remotely without granting shell access.
The API is implemented using Apache and a small set of PHP scripts. It is intentionally minimal, only accessible on the internal network, and protected with lightweight authorization.
The API exposes a limited and explicit set of actions. These include restarting Chromium, refreshing the currently displayed page, switching between predefined dashboard URLs, and turning the display on or off.
Display power control allows the screen to be switched off outside office hours and re-enabled automatically when needed. This avoids unnecessary screen wear while keeping the system itself running.
This control layer turns the iMac from a passive screen into a remotely recoverable and automatable appliance, without exposing system-level access.
If the API is not reachable, verify Apache binding and firewall rules on the internal network.
Step 12: Integration with Homey
Homey is our office automation hub. We already use it for everything from the Christmas tree (yes, already) to lighting, radio controls, and other routines.
Now Homey also controls the iMac display state and which status page is rendered. The local API makes the dashboard part of the broader Homey flows and automations.
Homey interacts with the dashboard exclusively through this API. This enables automations such as showing a different dashboard during incidents, refreshing the screen on a schedule, or powering the display based on office presence.
Step 13: Security hardening choices
Even though this is just a screen, security still matters.
- SSH access limited to the internal network
- Strong SSH configuration
- No root SSH login and no password logins
- Minimal installed packages
- Apache bound only to local interfaces
- Fail2ban monitoring the logs
- Regular unattended security updates
We also recommend verifying Fail2ban is active and monitoring SSH:
fail2ban-client status
fail2ban-client status sshdAutomation gotcha: cron permissions
We run some weekly automation via /etc/cron.d. One failure mode surprised us: cron ignores files that are group-writable or world-writable and logs 'INSECURE MODE'.
If your job never runs, check permissions first:
ls -l /etc/cron.d/your-job
chmod 600 /etc/cron.d/your-job
chown root:root /etc/cron.d/your-jobThis is a security feature. It prevents privilege escalation through writable cron definitions.
We only discovered the issue because Semonto Cron Job monitoring flagged a missed run. That feedback loop was worth it. Semonto Cron Job monitoring
Apple hardware quirks under Linux
Running Linux on Apple hardware works well, but there are a few non-obvious quirks worth calling out.
- Fan control is not handled correctly without macfanctld
- High-DPI internal panels may expose multiple logical outputs
- EFI boot order can change unexpectedly after firmware events
- Power management defaults assume a laptop or desktop, not an appliance
None of these are blockers, but they require explicit handling. Once addressed, the system behaves predictably.
Final behavior
- Power on
- Debian boots unattended
- Chromium launches full screen
- Dashboard fills the entire 5K display
- Automatic recovery on crashes
- Remote control via Homey
- No keyboard or mouse required
Next steps
This iMac now runs internal services like a VPN server, a Jenkins server, and a business insights tool. We also want to do some more fun things with the speakers, camera, microphone, AI projects, and more, which will be a topic of one of the next office hackathons. Stay tuned! (All within GDPR, of course.)
Sustainability and hardware reuse
This setup is a reminder that macOS end of life does not mean hardware end of life. By replacing the operating system and narrowing the purpose, we extended the useful life of an expensive device by years.
It is a practical example of sustainable hardware reuse in a modern office.