FreeBSD.software
Home/Guides/FreeBSD rc System: Service Management Guide
guide·2026-04-09·10 min read

FreeBSD rc System: Service Management Guide

FreeBSD rc system explained: rc.conf configuration, rc.d scripts, the service command, writing custom rc scripts, service dependencies, and advanced service management techniques.

FreeBSD rc System: Service Management Guide

FreeBSD's rc system manages everything that happens between kernel boot and login prompt. It starts services, configures network interfaces, mounts filesystems, sets sysctls, and runs periodic maintenance tasks. Unlike systemd on Linux, the rc system is built on shell scripts -- readable, debuggable, and predictable.

The rc system has three core components: /etc/rc.conf (what to start and how to configure it), /etc/rc.d/ and /usr/local/etc/rc.d/ (the scripts that do the work), and the service command (the interface you use to control everything).

This guide covers all three in depth, including how to write your own rc scripts.

rc.conf: The Central Configuration File

/etc/rc.conf is the single most important configuration file on a FreeBSD system. It controls which services start at boot, how network interfaces are configured, and system-wide settings.

Structure

sh
cat /etc/rc.conf

A typical production server's rc.conf:

shell
# Network hostname="web1.example.com" ifconfig_em0="inet 10.0.0.10 netmask 255.255.255.0" defaultrouter="10.0.0.1" # Services sshd_enable="YES" nginx_enable="YES" postgresql_enable="YES" pf_enable="YES" # Firewall pf_rules="/etc/pf.conf" # ZFS zfs_enable="YES" # Jails bastille_enable="YES" # Time ntpd_enable="YES" # Logging syslogd_flags="-ss" # Misc clear_tmp_enable="YES" sendmail_enable="NONE" dumpdev="AUTO"

Each line follows the pattern variable="value". The convention for enabling services is servicename_enable="YES".

rc.conf.local

For machine-specific overrides, use /etc/rc.conf.local. It is read after rc.conf and overrides any conflicting settings:

sh
# /etc/rc.conf -- base configuration (shared across fleet) # /etc/rc.conf.local -- per-machine overrides cat /etc/rc.conf.local
shell
hostname="web2.example.com" ifconfig_em0="inet 10.0.0.11 netmask 255.255.255.0"

Editing rc.conf with sysrc

Never edit rc.conf by hand in scripts. Use sysrc for safe, atomic modifications:

sh
# Set a value sysrc nginx_enable="YES" # Delete a value sysrc -x nginx_enable # Query a value sysrc nginx_enable # Show all settings sysrc -a # Edit rc.conf.local instead sysrc -f /etc/rc.conf.local hostname="web2.example.com" # Show where a value is defined sysrc -A nginx_enable

sysrc handles quoting, prevents duplicate entries, and validates syntax. It is the correct way to modify rc.conf programmatically.

rc.conf.d Directory

For complex configurations, you can split rc.conf into per-service files under /etc/rc.conf.d/:

sh
# Instead of putting everything in /etc/rc.conf: cat > /etc/rc.conf.d/nginx << 'EOF' nginx_enable="YES" nginx_flags="-c /usr/local/etc/nginx/nginx.conf" nginx_pidfile="/var/run/nginx.pid" EOF

Each file is named after the service and sourced when that service's rc script runs. This keeps configuration modular on complex systems.

The service Command

The service command is the primary interface for managing services:

sh
# Start a service service nginx start # Stop a service service nginx stop # Restart a service service nginx restart # Reload configuration (if supported) service nginx reload # Check service status service nginx status # Run a one-time command (even if not enabled in rc.conf) service nginx onestart service nginx onestop # List all enabled services service -e # List all available services service -l # Show the rc.d script path for a service service -r nginx

onestart vs start

service nginx start only works if nginx_enable="YES" is set in rc.conf. If you want to start a service temporarily without enabling it permanently:

sh
service nginx onestart

This is useful for testing. onestart runs the service without checking the _enable variable.

Checking What Is Running

sh
# All enabled services and their status service -e | while read svc; do service "$svc" status 2>/dev/null done # Simple process check pgrep -l nginx

rc.d Scripts: How Services Work

Every service on FreeBSD is managed by an rc.d script. Base system services live in /etc/rc.d/. Third-party services (from packages) live in /usr/local/etc/rc.d/.

Anatomy of an rc.d Script

Look at a real one:

sh
cat /usr/local/etc/rc.d/nginx
sh
#!/bin/sh # PROVIDE: nginx # REQUIRE: LOGIN cleanvar # KEYWORD: shutdown . /etc/rc.subr name="nginx" rcvar=nginx_enable command="/usr/local/sbin/nginx" pidfile="/var/run/nginx.pid" required_files="/usr/local/etc/nginx/nginx.conf" nginx_enable=${nginx_enable:-"NO"} nginx_flags=${nginx_flags:-""} extra_commands="reload configtest" reload_cmd="nginx_reload" configtest_cmd="nginx_configtest" nginx_reload() { echo "Reloading ${name}." ${command} -s reload } nginx_configtest() { echo "Checking ${name} configuration." ${command} -t } load_rc_config $name run_rc_command "$1"

Key elements:

  • PROVIDE -- The name this script provides (used for dependency resolution)
  • REQUIRE -- Services that must start before this one
  • KEYWORD -- Special flags (shutdown means run during shutdown)
  • . /etc/rc.subr -- Loads the rc framework functions
  • name -- The service name
  • rcvar -- The rc.conf variable that enables this service
  • command -- Path to the daemon binary
  • pidfile -- Where the daemon writes its PID
  • required_files -- Files that must exist for the service to start
  • extra_commands -- Additional commands beyond start/stop/restart
  • load_rc_config -- Loads variables from rc.conf
  • run_rc_command -- Executes the requested action

Dependency System

The REQUIRE and PROVIDE tags create a dependency graph. The rc system uses rcorder to determine the correct startup sequence:

sh
# Show the boot order rcorder /etc/rc.d/* /usr/local/etc/rc.d/*

Common dependency targets:

| Target | When |

|---|---|

| FILESYSTEMS | After filesystems are mounted |

| NETWORKING | After network interfaces are up |

| SERVERS | After basic server services |

| DAEMON | After daemon infrastructure |

| LOGIN | After login services (PAM, etc.) |

A service that needs networking and filesystems:

shell
# REQUIRE: NETWORKING FILESYSTEMS

A service that must start before another:

shell
# PROVIDE: mydb # REQUIRE: NETWORKING # In another script: # REQUIRE: mydb

Writing Custom rc.d Scripts

Minimal Script

Create /usr/local/etc/rc.d/myapp:

sh
#!/bin/sh # PROVIDE: myapp # REQUIRE: LOGIN NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="myapp" rcvar=myapp_enable command="/usr/local/bin/myapp" pidfile="/var/run/${name}.pid" myapp_enable=${myapp_enable:-"NO"} load_rc_config $name run_rc_command "$1"

Make it executable and enable:

sh
chmod +x /usr/local/etc/rc.d/myapp sysrc myapp_enable="YES" service myapp start

Script with Custom Start/Stop

For applications that do not daemonize themselves:

sh
#!/bin/sh # PROVIDE: myapp # REQUIRE: LOGIN NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="myapp" rcvar=myapp_enable command="/usr/local/bin/myapp" command_args="-c /usr/local/etc/myapp.conf" pidfile="/var/run/${name}.pid" myapp_enable=${myapp_enable:-"NO"} myapp_user=${myapp_user:-"www"} myapp_group=${myapp_group:-"www"} myapp_logfile=${myapp_logfile:-"/var/log/myapp.log"} start_cmd="${name}_start" stop_cmd="${name}_stop" status_cmd="${name}_status" myapp_start() { echo "Starting ${name}." /usr/sbin/daemon -f -p ${pidfile} -u ${myapp_user} \ ${command} ${command_args} >> ${myapp_logfile} 2>&1 } myapp_stop() { if [ -f ${pidfile} ]; then echo "Stopping ${name}." kill $(cat ${pidfile}) rm -f ${pidfile} else echo "${name} is not running." fi } myapp_status() { if [ -f ${pidfile} ] && kill -0 $(cat ${pidfile}) 2>/dev/null; then echo "${name} is running as pid $(cat ${pidfile})." else echo "${name} is not running." return 1 fi } load_rc_config $name run_rc_command "$1"

The daemon utility is key. It handles backgrounding, PID file creation, and user switching. Use it for any application that does not daemonize itself.

Script with Environment Variables

sh
#!/bin/sh # PROVIDE: myapp # REQUIRE: LOGIN NETWORKING # KEYWORD: shutdown . /etc/rc.subr name="myapp" rcvar=myapp_enable command="/usr/local/bin/myapp" pidfile="/var/run/${name}.pid" myapp_enable=${myapp_enable:-"NO"} myapp_env=${myapp_env:-""} myapp_chdir=${myapp_chdir:-"/usr/local/www/myapp"} start_cmd="${name}_start" myapp_start() { echo "Starting ${name}." cd ${myapp_chdir} /usr/bin/env ${myapp_env} /usr/sbin/daemon -f -p ${pidfile} \ ${command} ${command_args} } load_rc_config $name run_rc_command "$1"

Set environment variables in rc.conf:

sh
sysrc myapp_enable="YES" sysrc myapp_env="DATABASE_URL=postgres://localhost/mydb SECRET_KEY=abc123" sysrc myapp_chdir="/usr/local/www/myapp"

Script with Health Checks

sh
#!/bin/sh # PROVIDE: myapp # REQUIRE: LOGIN NETWORKING postgresql # KEYWORD: shutdown . /etc/rc.subr name="myapp" rcvar=myapp_enable command="/usr/local/bin/myapp" pidfile="/var/run/${name}.pid" myapp_enable=${myapp_enable:-"NO"} extra_commands="health" health_cmd="${name}_health" myapp_health() { local response response=$(fetch -qo - http://127.0.0.1:8080/health 2>/dev/null) if [ "$response" = "ok" ]; then echo "${name} is healthy." return 0 else echo "${name} health check failed." return 1 fi } load_rc_config $name run_rc_command "$1"

Use it:

sh
service myapp health

Advanced rc.conf Techniques

Profiles

Some services support profiles for running multiple instances:

sh
sysrc jail_enable="YES" sysrc jail_list="web db cache" sysrc jail_web_hostname="web.example.com" sysrc jail_db_hostname="db.example.com"

Conditional Configuration

rc.conf supports shell-like conditional logic when sourced:

sh
# /etc/rc.conf.d/nginx case $(hostname) in web*) nginx_enable="YES" nginx_flags="-c /usr/local/etc/nginx/web.conf" ;; proxy*) nginx_enable="YES" nginx_flags="-c /usr/local/etc/nginx/proxy.conf" ;; *) nginx_enable="NO" ;; esac

Service Flags

Most services accept flags through rc.conf:

sh
sysrc sshd_flags="-o Port=2222 -o PermitRootLogin=no" sysrc syslogd_flags="-ss" # Disable remote logging sysrc ntpd_flags="-g" # Allow large initial offset sysrc sendmail_enable="NONE" # Disable all sendmail components

Network Interface Configuration

rc.conf handles all network configuration:

sh
# Static IP sysrc ifconfig_em0="inet 10.0.0.10 netmask 255.255.255.0" sysrc defaultrouter="10.0.0.1" # DHCP sysrc ifconfig_em0="DHCP" # VLAN sysrc vlans_em0="100 200" sysrc ifconfig_em0_100="inet 10.1.0.10/24" sysrc ifconfig_em0_200="inet 10.2.0.10/24" # Bridge sysrc cloned_interfaces="bridge0" sysrc ifconfig_bridge0="addm em0 addm em1 up" # Wireless sysrc wlans_iwn0="wlan0" sysrc ifconfig_wlan0="WPA DHCP"

Debugging rc Scripts

Verbose Boot

To see every rc script as it executes during boot:

sh
# At the boot loader prompt set boot_verbose="YES" boot

Or permanently:

sh
sysrc rc_startmsgs="YES"

Running Scripts in Debug Mode

sh
# Trace a specific script sh -x /usr/local/etc/rc.d/nginx start # Check syntax without running sh -n /usr/local/etc/rc.d/myapp # Check dependency order rcorder /etc/rc.d/* /usr/local/etc/rc.d/* | head -50

Common Problems

Service will not start -- "not enabled"

sh
# Check if enabled sysrc nginx_enable # If NO, either enable it or use onestart: service nginx onestart

PID file stale after crash

sh
# Remove stale PID file rm /var/run/nginx.pid service nginx start

Service starts but immediately exits

sh
# Check logs tail -50 /var/log/messages tail -50 /var/log/myapp.log # Run the command manually to see errors su -m www -c '/usr/local/bin/myapp -c /usr/local/etc/myapp.conf'

Dependency order wrong

sh
# Check what rcorder produces rcorder /etc/rc.d/* /usr/local/etc/rc.d/* | grep -A2 -B2 myapp

The rc System Boot Sequence

Understanding the boot sequence helps debug startup issues:

  1. Kernel loads and runs /sbin/init
  2. init reads /etc/ttys and runs /etc/rc
  3. /etc/rc sources /etc/rc.subr and /etc/rc.conf
  4. rcorder determines script execution order from /etc/rc.d/
  5. Scripts run in dependency order: filesystems, networking, base services
  6. /usr/local/etc/rc.d/ scripts run after base system scripts
  7. Login prompt appears

The entire sequence is shell scripts. If something goes wrong, you can read the scripts and understand exactly what happened.

Comparison with systemd

| Feature | FreeBSD rc | Linux systemd |

|---|---|---|

| Language | Shell scripts | Unit files + C daemon |

| Dependencies | REQUIRE/PROVIDE tags | After=/Before= directives |

| Parallel startup | No (sequential) | Yes |

| Socket activation | No | Yes |

| Resource limits | Via login.conf/rctl | Via cgroups |

| Logging | syslog | journald |

| Configuration | /etc/rc.conf (text) | Unit files + dbus |

| Complexity | Low | High |

| Debuggability | Excellent (sh -x) | Moderate (journalctl) |

FreeBSD's rc system is simpler, more transparent, and easier to debug. Systemd is more feature-rich and starts services faster through parallelization. For server workloads where boot time is irrelevant (servers run for months), rc's simplicity is an advantage.

FAQ

How do I make a service start at boot?

Set servicename_enable="YES" in /etc/rc.conf using sysrc:

sh
sysrc nginx_enable="YES"

Then either reboot or start it now with service nginx start.

What is the difference between /etc/rc.d/ and /usr/local/etc/rc.d/?

/etc/rc.d/ contains scripts for the base system (sshd, syslogd, cron, networking). /usr/local/etc/rc.d/ contains scripts for third-party packages installed via pkg or ports. Never put custom scripts in /etc/rc.d/ -- use /usr/local/etc/rc.d/.

Can I run services in parallel on FreeBSD?

The default rc system runs services sequentially. There is no built-in parallel execution like systemd. In practice, this adds a few seconds to boot time -- negligible for servers. If parallel startup matters, look at rc.d/rcorder with the -p flag (experimental).

How do I disable sendmail completely?

sh
sysrc sendmail_enable="NONE" sysrc sendmail_submit_enable="NO" sysrc sendmail_outbound_enable="NO" sysrc sendmail_msp_queue_enable="NO"

How do I view service logs?

FreeBSD services log to syslog by default. Check /var/log/messages and /var/log/daemon.log. Some services log to their own files (check the service's configuration). Use tail -f /var/log/messages to watch logs in real time.

Can I use the rc system inside jails?

Yes. Jails have their own /etc/rc.conf and run their own rc scripts. Services inside a jail are managed with service just like on the host. The jail's rc.conf is independent of the host's rc.conf.

Get more FreeBSD guides

Weekly tutorials, security advisories, and package updates. No spam.