English
macOS launchctl Service Dependency Configuration in Practice: Taking Kafka's Dependency on Zookeeper as an Example

Overview
Use in a macOS environment launchctl When managing services, we often encounter Service dependency the problem.
with Kafka For example: Kafka must depend on Zookeeper The service has already started successfully; otherwise, Kafka would fail to start because it cannot connect to Zookeeper.
If directly through launchctl Configure both services to start on boot. Since the startup order cannot be controlled, the following situation is very likely to occur:
Zookeeper has not started yet → Kafka attempts to start → Failed to connect to Zookeeper → Kafka failed to start ❌This article will demonstrate how to use launchctl the plist configuration, so that Kafka at startup Wait for Zookeeper to be ready before starting., fundamentally solving the boot order problem.
Implementation plan
Core configuration items
at launchd In the plist configuration, service dependencies are implemented through the following two key configurations:
| Configuration item | Effect |
|---|---|
KeepAlive+OtherJobEnabled | Specify the Label of the target service that the current service depends on. The current service will remain running only when the target service is enabled. |
After | Explicitly specify which services the current service starts after (startup order control) |
When the two are used together, it can ensure:
Startup order: Start the dependent service (Zookeeper) first.
runtime dependency: When a dependent service exits abnormally, the current service (Kafka) will also stop automatically (optional behavior)
1. Zookeeper Service Definition
Zookeeper, as a service that is depended on, does not require special configuration; simply define it in the standard way:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Description</key>
Zookeeper - is an open-source distributed coordination service that provides efficient and reliable coordination capabilities for distributed applications. It was originally developed by Yahoo and later became a top-level project of the Apache Software Foundation.
<key>Disabled</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>apache.org.zookeeper</string>
<key>ProcessType</key>
<string>Background</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/kafka/bin/zookeeper-server-start.sh</string>
<string>/usr/local/opt/kafka/config/zookeeper.properties</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/zookeeper.log</string>
<key>StandardOutPath</key>
<string>/usr/local/var/log/zookeeper.log</string>
<key>Version</key>
<string>v3.8.4</string>
</dict>
</plist>2. Kafka Service Definition (with dependency configuration)
The configuration of Kafka services is the core of this article, with the focus on KeepAlive and After Field:
xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Description</key>
Kafka - is an open-source distributed event streaming platform used by thousands of companies for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.
<key>Disabled</key>
<true/>
<key>KeepAlive</key>
<dict>
<key>OtherJobEnabled</key>
<string>apache.org.zookeeper</string>
</dict>
<key>After</key>
<array>
<string>apache.org.zookeeper</string>
</array>
<key>Label</key>
<string>apache.org.kafka</string>
<key>ProcessType</key>
<string>Background</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/opt/kafka/bin/kafka-server-start.sh</string>
<string>/usr/local/opt/kafka/config/server.properties</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/usr/local/var/log/kafka.log</string>
<key>StandardOutPath</key>
<string>/usr/local/var/log/kafka.log</string>
<key>Version</key>
<string>v3.9.0</string>
</dict>
</plist>Dependency Configuration Explained
xml
<key>KeepAlive</key>
<dict>
<key>OtherJobEnabled</key>
<string>apache.org.zookeeper</string>
</dict>
<key>After</key>
<array>
<string>apache.org.zookeeper</string>
</array>| field | value | Meaning |
|---|---|---|
KeepAlive OtherJobEnabled | apache.org.zookeeper | Kafka will check whether the Zookeeper service is in enabled status; Kafka will keep running only when Zookeeper is enabled |
After | [apache.org.zookeeper] | Clearly inform launchd: Kafka must be started after ZooKeeper to ensure the startup order. |
Comparison: no dependencies vs with dependencies
Dependency-free configuration (conventional method):
xml
<key>KeepAlive</key>
<true/>Under this configuration, Kafka does not check the status of any external services. During boot, it may start at the same time as Zookeeper or before Zookeeper, resulting in startup failure.
With dependency configuration (the method in this article):
xml
<key>KeepAlive</key>
<dict>
<key>OtherJobEnabled</key>
<string>apache.org.zookeeper</string>
</dict>
<key>After</key>
<array>
<string>apache.org.zookeeper</string>
</array>Kafka will wait for ZooKeeper to start and become available before starting itself, completely solving the startup order issue.
3. Loading service
After configuration is complete, run the following command to load the service:
bash
# Load the Kafka service (same for Zookeeper)
launchctl load ~/Library/LaunchAgents/apache.org.kafka.plist
# Or use bootstrap (recommended for macOS 10.10+)
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/apache.org.kafka.plist4. Verify the effect
bash
# Check the Zookeeper service status.
launchctl print gui/$UID/apache.org.zookeeper
# View Kafka service status (you can see the dependencies)
launchctl print gui/$UID/apache.org.kafka
# View service runtime logs
tail -f /usr/local/var/log/zookeeper.log
tail -f /usr/local/var/log/kafka.logAfter the system restarts,launchd will start in the following order:
1. Zookeeper started ✅
2. Zookeeper status changed to enabled
3. Kafka detected that Zookeeper is ready
4. Kafka startup ✅Extension: Other common service dependency scenarios
| Scene | Dependency relationship | Configuration method |
|---|---|---|
| Web services + database | Nginx depends on MySQL | OtherJobEnabled→com.mysql.mysqld |
| Message Queue + Storage | RabbitMQ depends on PostgreSQL | OtherJobEnabled → org.postgresql.postgres |
| Monitoring tool + monitored service | Prometheus relies on Node Exporter | OtherJobEnabled→io.prometheus.node_exporter |
| Microservice call chain | Service B depends on Service A | Same as above, progressing step by step. |
Precautions
plist file location: This example uses a user-level service, and the plist is stored in
~/Library/LaunchAgents/. If it is a system-level service, it needs to be placed in/Library/LaunchDaemons/and usesudoLoading.Disabled field: in the example
Disabledfortrue, indicating that the service is disabled by default. You can enable it with the following command:
bash
launchctl enable gui/$UID/apache.org.kafkacircular dependency Avoid a circular dependency where A depends on B and B depends on A, which will cause the service to fail to start normally.
Dependent service Label must be accurate:
OtherJobEnabledandAfterThe Label filled in must be exactly the same as the Label of the dependent service.
Summary
on macOS via
launchctlManaging service dependencies is fundamentally about configuration.KeepAlive+OtherJobEnabledandAfterCombination.OtherJobEnabledSpecify the Label of the target service to depend on, ensuring that the current service remains running only when the target service is enabled.AfterControl the startup order to ensure that dependent services start first.Through this solution, the startup order problem of services such as Kafka depending on Zookeeper can be perfectly solved.