Daemon Mode Implementation

February 1, 2026 ยท View on GitHub

This document describes the daemon mode implementation for Synapse.

Overview

Synapse now supports running as a daemon (background process) using the daemonize crate. This allows Synapse to run as a system service with proper privilege dropping and process management.

Features

  • Background execution: Runs as a detached background process
  • PID file management: Creates and manages PID files for process control
  • Privilege dropping: Can drop privileges to a specified user and group after initialization
  • Output redirection: Redirects access logs and error logs to configurable log files
  • Working directory: Configurable working directory for the daemon
  • Signal handling: Proper signal handling for graceful shutdown

Configuration

Configuration File (YAML)

daemon:
  enabled: false                        # Enable daemon mode
  pid_file: "/var/run/synapse.pid"       # PID file path
  working_directory: "/"               # Working directory
  access_log: "/var/log/synapse/access.log"  # Access log file (raw JSON)
  error_log: "/var/log/synapse/error.log"    # Error log file
  user: "nobody"                       # User to run as (optional)
  group: "daemon"                      # Group to run as (optional)
  chown_pid_file: true                # Change PID file ownership to user/group

Command Line Arguments

  • --daemon, -d - Run as daemon in background
  • --daemon-pid-file <PATH> - PID file path (default: /var/run/synapse.pid)
  • --daemon-working-dir <PATH> - Working directory (default: /)
  • --daemon-access-log <PATH> - Access log file (default: /var/log/synapse/access.log)
  • --daemon-error-log <PATH> - Error log file (default: /var/log/synapse/error.log)
  • --daemon-user <USER> - User to run as (e.g., nobody)
  • --daemon-group <GROUP> - Group to run as (e.g., daemon)

Environment Variables

  • AX_DAEMON_ENABLED - Enable daemon mode (true/false)
  • AX_DAEMON_PID_FILE - PID file path
  • AX_DAEMON_WORKING_DIRECTORY - Working directory
  • AX_DAEMON_ACCESS_LOG - Access log file
  • AX_DAEMON_ERROR_LOG - Error log file
  • AX_DAEMON_USER - User to run as
  • AX_DAEMON_GROUP - Group to run as
  • AX_DAEMON_CHOWN_PID_FILE - Change PID file ownership (true/false)

Usage Examples

Basic Daemon Mode

synapse --daemon --iface eth0 --upstream "http://127.0.0.1:8081" --arxignis-api-key "your-key"

Custom Daemon Settings

synapse --daemon \
  --daemon-pid-file /var/run/synapse.pid \
  --daemon-working-dir / \
  --daemon-access-log /var/log/synapse/access.log \
  --daemon-error-log /var/log/synapse/error.log \
  --daemon-user nobody \
  --daemon-group daemon \
  --iface eth0 --upstream "http://127.0.0.1:8081" --arxignis-api-key "your-key"

With Configuration File

# config.yaml
daemon:
  enabled: true
  pid_file: "/var/run/synapse.pid"
  working_directory: "/"
  access_log: "/var/log/synapse/access.log"
  error_log: "/var/log/synapse/error.log"
  user: "nobody"
  group: "daemon"
  chown_pid_file: true

# Run with config file
synapse --config config.yaml

With Environment Variables

export AX_DAEMON_ENABLED="true"
export AX_DAEMON_PID_FILE="/var/run/synapse.pid"
export AX_DAEMON_USER="nobody"
export AX_DAEMON_GROUP="daemon"

synapse --iface eth0 --upstream "http://127.0.0.1:8081" --arxignis-api-key "your-key"

Process Management

Starting the Daemon

synapse --daemon --config /etc/synapse/config.yaml

Stopping the Daemon

# Using PID file
kill $(cat /var/run/synapse.pid)

# Or send SIGTERM
kill -TERM $(cat /var/run/synapse.pid)

# Graceful shutdown with SIGINT
kill -INT $(cat /var/run/synapse.pid)

Checking Status

# Check if process is running
ps aux | grep synapse

# Or check PID file
if [ -f /var/run/synapse.pid ]; then
    pid=$(cat /var/run/synapse.pid)
    if ps -p $pid > /dev/null; then
        echo "Synapse is running (PID: $pid)"
    else
        echo "Synapse is not running (stale PID file)"
    fi
else
    echo "Synapse is not running"
fi

Viewing Logs

In daemon mode, logs are split:

  • access_log (/var/log/synapse/access.log) - Access log JSON (one line per request)
  • error_log (/var/log/synapse/error.log) - All other logs (info/debug/warn/error)
# Tail application logs (primary log file)
tail -f /var/log/synapse/access.log

# Tail error output (panics, system errors)
tail -f /var/log/synapse/error.log

# View both logs simultaneously
tail -f /var/log/synapse/access.log /var/log/synapse/error.log

Note: Access logs are written to stdout (raw JSON). All other logs go to stderr.

Security Considerations

Privilege Dropping

When running as daemon with a privileged user (e.g., root) to bind to ports < 1024 or attach XDP programs, it's recommended to drop privileges after initialization:

synapse --daemon \
  --daemon-user nobody \
  --daemon-group daemon \
  --iface eth0 --upstream "http://127.0.0.1:8081" --arxignis-api-key "your-key"

This will:

  1. Start as root (or privileged user)
  2. Bind to privileged ports (80, 443)
  3. Attach XDP programs to network interfaces
  4. Drop privileges to nobody:daemon
  5. Continue running as unprivileged user

File Permissions

Ensure proper permissions for daemon files:

# Create log directory
sudo mkdir -p /var/log/synapse
sudo chown nobody:daemon /var/log/synapse
sudo chmod 755 /var/log/synapse

# Create PID directory
sudo mkdir -p /var/run
sudo chmod 755 /var/run

# Set up log files
sudo touch /var/log/synapse/access.log /var/log/synapse/error.log
sudo chown nobody:daemon /var/log/synapse/access.log /var/log/synapse/error.log
sudo chmod 644 /var/log/synapse/access.log /var/log/synapse/error.log

Systemd Integration

Create a systemd service file for easier management:

# /etc/systemd/system/synapse.service
[Unit]
Description=Synapse Reverse Proxy and Firewall
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/synapse.pid
ExecStart=/usr/local/bin/synapse --daemon --config /etc/synapse/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s

# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/synapse /var/run

[Install]
WantedBy=multi-user.target

Manage with systemd:

# Enable service
sudo systemctl enable synapse

# Start service
sudo systemctl start synapse

# Stop service
sudo systemctl stop synapse

# Restart service
sudo systemctl restart synapse

# View status
sudo systemctl status synapse

# View logs
sudo journalctl -u synapse -f

Implementation Details

Architecture

The daemon mode implementation uses a two-phase startup:

  1. Pre-daemonization phase:

    • Parse command line arguments
    • Load configuration
    • Validate settings
    • If daemon mode enabled, call daemonize() before starting Tokio runtime
  2. Post-daemonization phase:

    • Initialize logger
    • Start Tokio runtime
    • Run application logic

This ensures daemonization happens before any async operations, as required by the daemonize crate.

Key Files Modified

  • Cargo.toml - Added daemonize = "0.5.0" dependency
  • src/cli.rs - Added DaemonConfig struct and CLI arguments
  • src/main.rs - Restructured to support pre-tokio daemonization
  • config.yaml - Added daemon configuration section
  • config_example.yaml - Added daemon examples
  • README.md - Added daemon mode documentation

Signal Handling

The application already has proper signal handling with tokio::signal::ctrl_c(). When running as daemon:

  • SIGINT (Ctrl+C) triggers graceful shutdown
  • SIGTERM triggers graceful shutdown
  • XDP programs are properly detached on shutdown

Troubleshooting

Daemon Won't Start

Check:

  1. Log files for errors: cat /var/log/synapse.err
  2. Permissions on log directory and PID file location
  3. User/group exists: id nobody
  4. Configuration file is valid: synapse --config /etc/synapse/config.yaml (without --daemon)

Permission Denied Errors

If you see permission errors:

  • Ensure log directory is writable by daemon user
  • Ensure PID file location is writable
  • Check that user/group specified exists
  • Verify file system permissions

Stale PID File

If daemon won't start due to existing PID file:

# Check if process is actually running
ps -p $(cat /var/run/synapse.pid)

# If not running, remove stale PID file
sudo rm /var/run/synapse.pid

# Then start daemon
synapse --daemon --config /etc/synapse/config.yaml

References