# Openwrt Initd

> Use when OpenWrt에 boot 시 자동 시작과 프로세스 감시(supervision)가 포함된 background service를 설치. agent, 모니터링 도구, crash 시 respawn과 persistent logging이 필요한 장기 실행 background 프로세스에 사용.

- Skill: `deuxksy/openwrt-initd` (Agent Skill)
- Install (CLI): `npx skillmds@latest add deuxksy/openwrt-initd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/deuxksy/openwrt-initd/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: deuxksy (https://skillmd.com/u/deuxksy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/deuxksy/openwrt-initd

---


# OpenWrt Init.d Service Installation

## Overview

Install background services on OpenWrt as init.d scripts with procd supervision. Services auto-start on boot, log to `/var/log/`, and respawn on failure.

## When to Use

Need to run service on OpenWrt? This skill
Is it background process? This skill
Need auto-start on boot? This skill
Need crash recovery? This skill

Use cases:
- Monitoring agents (beszel-agent, telegraf, etc.)
- Custom daemons
- Network services
- Data collectors

NOT for:
- One-time scripts (use /etc/rc.local)
- Interactive commands (run manually)
- CGIs/FastCGI (use uhttpd configuration)

## Core Pattern

OpenWrt init.d = /etc/init.d/<name> + START priority + start() function

```sh
#!/bin/sh /etc/rc.common

START=99              # Start late (network ready)
USE_PROCD=1          # Enable procd supervision

start_service() {
    procd_open_instance
    procd_set_param command "/usr/bin/service"
    procd_set_param respawn 3600 5 5
    procd_set_param stdout "/var/log/service.log"
    procd_set_param stderr "/var/log/service.log"
    procd_close_instance
}
```

## Quick Reference

| Task | Command |
|------|--------|
| Install script | cat > /etc/init.d/service-name |
| Set executable | chmod +x /etc/init.d/service-name |
| Enable autostart | /etc/init.d/service-name enable |
| Start service | /etc/init.d/service-name start |
| Check status | pgrep -f /usr/bin/service or cat /var/run/service.pid |
| Service info | /etc/init.d/service-name info |
| Stop service | /etc/init.d/service-name stop |

## UCI Configuration (Optional)

For configurable services, use UCI:

```sh
# Create config
cat > /etc/config/service
config service 'main'
    option port '8080'
    option token 'abc123'

# In init.d script
local port=$(uci get service.main.port 2>/dev/null)

# Update config
uci set service.main.port='8080'
uci commit service
```

## Log Management

```sh
# Create log directory with error checking
mkdir -p /var/log || {
    echo "Error: Cannot create log directory" >&2
    return 1
}

# Set restrictive permissions
umask 077
touch "$LOG_FILE"

# Verify logs
tail -n 50 /var/log/service.log
```

## Direct Logging (When procd stdout/stderr Fails)

If procd log redirection doesn't work, use background process with redirection:

```sh
start() {
    mkdir -p /var/log /var/run || return 1

    # Stop existing
    stop

    # Start with logging
    "$PROG" \
        -l "$port" \
        -k "$ssh_key" \
        -t "$token" \
        -u "$hub_url" \
        >> "$LOG_FILE" 2>&1 &

    local pid=$!
    echo "$pid" > "$PID_FILE"
}
```

## Common Pitfalls

| Issue | Solution |
|-------|----------|
| Service won't start | Check shebang: #!/bin/sh /etc/rc.common |
| Enable doesn't persist | Verify symlink: ls -la /etc/rc.d/*service* |
| No logs created | Use direct redirection: >> "$LOG_FILE" 2>&1 & |
| Permissions too open | Set umask: umask 077 before touch |
| Binary not found | Copy to /usr/bin/ not /usr/local/bin |
| procd_status not found | Use pgrep or PID file instead |
| `status()` ignored by rc.common | Rename to `info()` — rc.common has built-in `status` |
| Wrong CLI flags | Run `<binary> --help` before writing script to verify flags |
| Data lost on reboot | `/var` → `/tmp` symlink; use `/etc/<service>/` for persistent data |

## Service Script Template

```sh
#!/bin/sh /etc/rc.common

START=99
STOP=10

PROG="/usr/bin/service"
LOG_FILE="/var/log/service.log"
PID_FILE="/var/run/service.pid"
DATA_DIR="/etc/service"

start() {
    mkdir -p /var/log /var/run "$DATA_DIR" || return 1

    # Read UCI config (optional)
    local setting=$(uci get service.main.setting 2>/dev/null)

    # Validate required config
    [ -z "$setting" ] && echo "Error: setting not configured" && return 1

    stop

    umask 077
    touch "$LOG_FILE"

    export DATA_DIR
    "$PROG" \
        --config "$setting" \
        >> "$LOG_FILE" 2>&1 &

    local pid=$!
    echo "$pid" > "$PID_FILE"
    sleep 1

    if kill -0 "$pid" 2>/dev/null; then
        echo "Service started (PID: $pid)"
        return 0
    else
        echo "Failed to start service" >&2
        rm -f "$PID_FILE"
        return 1
    fi
}

stop() {
    if [ -f "$PID_FILE" ]; then
        kill $(cat "$PID_FILE") 2>/dev/null
        rm -f "$PID_FILE"
    fi
    pkill -9 -f "$PROG" 2>/dev/null
}

restart() {
    stop
    sleep 1
    start
}

info() {
    if [ -f "$PID_FILE" ]; then
        local pid=$(cat "$PID_FILE")
        if kill -0 "$pid" 2>/dev/null; then
            echo "[OK] service: running (PID: $pid)"
        else
            echo "[STOPPED] service: stopped (stale PID file)"
            rm -f "$PID_FILE"
        fi
    else
        local pid=$(pgrep -f "$PROG" | head -1)
        if [ -n "$pid" ]; then
            echo "[OK] service: running (PID: $pid)"
        else
            echo "[STOPPED] service: stopped"
        fi
    fi

    echo ""
    echo "=== Latest 50 log lines ==="
    if [ -f "$LOG_FILE" ]; then
        tail -n 50 "$LOG_FILE"
    else
        echo "Log file not found: $LOG_FILE"
    fi
}
```

