Monit's default behavior of running as root creates security concerns when managing services like MySQL, Apache, or custom applications. While this gives Monit the necessary permissions to control system processes, it's not ideal for actually running those services.
The solution lies in Monit's start
program directive. Here's the basic syntax:
check process mysqld
matching "mysqld"
start program = "/etc/init.d/mysql start" as uid "mysql" and gid "mysql"
stop program = "/etc/init.d/mysql stop"
For more complex scenarios, you might need to combine user context with environment variables:
check process rails_app
matching "mongrel"
start program = "/bin/bash -c 'cd /apps/rails/current && bundle exec mongrel_rails start -e production -p 8000 -a 127.0.0.1 -d'"
as uid "deploy" and gid "deploy"
with timeout 60 seconds
stop program = "/bin/bash -c 'cd /apps/rails/current && bundle exec mongrel_rails stop'"
as uid "deploy" and gid "deploy"
When switching users, environment variables might be lost. Use the with
clause to preserve them:
check process sidekiq
matching "sidekiq"
start program = "/usr/local/bin/sidekiq -C /apps/config/sidekiq.yml"
as uid "appuser" and gid "appgroup"
with HOME "/home/appuser"
with PATH "/usr/local/bin:/usr/bin:/bin"
with RAILS_ENV "production"
Remember that the target user must have appropriate permissions:
- Execution rights on the binary
- Read access to configuration files
- Write access to PID files and log directories
If processes fail to start, check:
# Verify user switching
sudo -u username -g groupname /path/to/command
# Check Monit logs
tail -f /var/log/monit.log
# Test process startup manually
su - username -c "/full/path/to/command"
When configuring Monit to manage services, a common security concern arises: Monit typically executes as root, but we need certain processes (like MySQL, Apache, or custom applications) to run under specific user accounts. Here's how to properly implement this while maintaining system security.
The key to solving this lies in Monit's process control directives:
check process mysqld
matching "mysql"
start program = "/etc/init.d/mysql start" as uid "mysql" and gid "mysql"
stop program = "/etc/init.d/mysql stop" as uid "mysql" and gid "mysql"
if failed port 3306 protocol mysql then restart
if 5 restarts within 5 cycles then timeout
For different service types, the implementation varies slightly:
For Systemd Services
check process nginx
matching "nginx"
start program = "/bin/systemctl start nginx" as uid "www-data" and gid "www-data"
stop program = "/bin/systemctl stop nginx"
if failed port 80 protocol http then restart
For Direct Binary Execution
check process custom_app
matching "custom_app"
start program = "/usr/bin/sudo -u appuser /path/to/custom_app" as uid "root"
stop program = "/usr/bin/killall custom_app" as uid "root"
if failed port 9000 then restart
When dealing with more complex scenarios:
check process redis
matching "redis-server"
start program = "/usr/bin/sudo -u redis -g redis /usr/bin/redis-server /etc/redis/redis.conf" as uid "root"
stop program = "/usr/bin/sudo -u redis /usr/bin/redis-cli shutdown" as uid "root"
if failed port 6379 then restart
if cpu > 80% for 3 cycles then alert
When implementing user context changes:
- Always verify the target user has minimal required permissions
- Use dedicated system accounts for services (never regular user accounts)
- Consider filesystem permissions for any resources the service needs to access
- Test stop/start operations thoroughly before relying on Monit
If services fail to start properly:
- Check Monit logs (
/var/log/monit.log
) - Verify the user exists (
id username
) - Test the start command manually with the target user (
sudo -u username command
) - Review process capabilities and SELinux contexts if applicable
For complex startup requirements, consider using wrapper scripts:
#!/bin/bash
# /usr/local/bin/start_custom.sh
sudo -u appuser -g appgroup /path/to/application --config /etc/app.conf
Then reference in Monit:
start program = "/usr/local/bin/start_custom.sh" as uid "root"