File System Layout

βœ… Validated against Unraid 7.2.3 - Directory structures and paths verified on live systems.

Understanding where files are stored and why is crucial for Unraid plugin development. Unraid runs from RAM, which creates unique challenges and opportunities.

The RAM-Based Architecture

Unraid boots from a USB flash drive into RAM. This means:

  • Changes to most files are lost on reboot
  • The USB flash drive is the only persistent storage during boot
  • Plugins must be reinstalled on every boot
flowchart TB
    subgraph USB["USB Flash Drive /boot"]
        subgraph Persistent["Persistent - survives reboot"]
            P1["/boot/config/plugins/myplugin/"]
            P2["myplugin.cfg (user settings)"]
            P3["myplugin-package.txz (cached)"]
        end
    end
    
    USB -->|"Installed on boot"| RAM
    
    subgraph RAM["RAM Disk"]
        subgraph Volatile["Volatile - rebuilt each boot"]
            R1["/usr/local/emhttp/plugins/myplugin/"]
            R2["myplugin.page (web UI)"]
            R3["scripts/ (shell scripts)"]
            R4["php/ (PHP code)"]
        end
    end
    
    style USB fill:#4a9eff,color:#fff
    style RAM fill:#ff9800,color:#fff
    style Persistent fill:#4caf50,color:#fff
    style Volatile fill:#f44336,color:#fff

πŸ“· Screenshot needed: File listing of /boot/config/plugins/ directory

Boot plugins directory

Key Directories

/boot/config/plugins/

Location: USB Flash Drive
Persistence: βœ… Survives reboot

This is where plugin files are stored on the boot device:

/boot/config/plugins/
β”œβ”€β”€ myplugin.plg                    # The plugin installer file
β”œβ”€β”€ myplugin/                       # Plugin's persistent storage
β”‚   β”œβ”€β”€ myplugin.cfg               # User configuration
β”‚   β”œβ”€β”€ myplugin-package-1.0.txz   # Cached package (optional)
β”‚   └── data/                      # Plugin data files
└── [other-plugins...]

Use for:

  • User configuration files
  • Cached downloads (packages, icons)
  • Small data files that must persist
  • State files

Avoid:

  • Large data files (limited USB space)
  • Frequently written files (USB wear)
  • Log files

πŸ“· Screenshot needed: File listing of /boot/config/plugins/ directory

USB plugins directory

/usr/local/emhttp/plugins/

Location: RAM Disk
Persistence: ❌ Rebuilt on boot

This is where active plugin files live:

/usr/local/emhttp/plugins/myplugin/
β”œβ”€β”€ myplugin.page                  # Main UI page
β”œβ”€β”€ myplugin.settings.page         # Settings page
β”œβ”€β”€ default.cfg                    # Default configuration
β”œβ”€β”€ README.md                      # Shown in Plugin Manager
β”œβ”€β”€ php/
β”‚   β”œβ”€β”€ exec.php                   # AJAX endpoint
β”‚   └── helpers.php                # Utility functions
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ start.sh                   # Service start script
β”‚   └── status.sh                  # Status check
β”œβ”€β”€ event/
β”‚   β”œβ”€β”€ started                    # Array started handler
β”‚   └── stopping_docker            # Pre-Docker-stop handler
β”œβ”€β”€ javascript/
β”‚   └── custom.js                  # Client-side scripts
└── styles/
    └── custom.css                 # Plugin styles

Contains:

  • Web UI pages (.page files)
  • PHP scripts
  • Shell scripts
  • Event handlers
  • Static assets (JS, CSS, images)
  • Default configuration

πŸ“· Screenshot needed: File listing of /usr/local/emhttp/plugins/ structure

Emhttp plugins directory

/var/log/plugins/

Location: RAM Disk
Persistence: ❌ Cleared on reboot

Contains symlinks indicating which plugins are installed:

/var/log/plugins/
β”œβ”€β”€ myplugin.plg -> /boot/config/plugins/myplugin.plg
└── [other-plugins.plg...]

The plugin manager checks this directory to know what’s installed.

/tmp/plugins/

Location: RAM Disk
Persistence: ❌ Temporary

Used during plugin operations:

  • Downloaded plugin files for updates
  • Temporary extraction location

/boot/config/plugins-*/

Special directories on the USB flash:

Directory Purpose
/boot/config/plugins/ Active plugins
/boot/config/plugins-error/ Plugins that failed to install
/boot/config/plugins-removed/ Uninstalled plugins
/boot/config/plugins-stale/ Superseded plugin versions

Data Storage Options

For Configuration

Store user settings on the USB flash:

/boot/config/plugins/myplugin/myplugin.cfg

Format:

SETTING_ONE="value"
SETTING_TWO="another value"
ENABLED="true"

Reading in PHP:

$cfg = parse_plugin_cfg('myplugin');
echo $cfg['SETTING_ONE'];

For Application Data

For larger data that needs to persist:

Option 1: User Share (Recommended)

/mnt/user/appdata/myplugin/

Option 2: Cache Drive

/mnt/cache/appdata/myplugin/

Option 3: Array Share

/mnt/user/myplugin-data/

For Logs

Use the system logger (writes to RAM, available in syslog):

logger "myplugin: Your log message"
logger -t myplugin "Tagged message"

Or write to a temporary file:

echo "Log message" >> /tmp/myplugin.log

For Runtime State

For PID files, state tracking:

# PID file
/var/run/myplugin.pid

# Lock files
/var/lock/myplugin.lock

# Temporary state
/tmp/myplugin/

File Permissions

Package Files

When building your package:

# Make scripts executable
chmod +x scripts/*.sh
chmod +x event/*

# Standard file permissions
chmod 644 *.page
chmod 644 *.cfg
chmod 644 php/*.php

# Make PHP exec scripts executable
chmod +x php/exec.php

Configuration Files

User config files on flash:

# Secure user config
chmod 600 /boot/config/plugins/myplugin/myplugin.cfg

Package Directory Structure

Your .txz package should extract to mirror the emhttp structure:

[package root]/
└── usr/
    └── local/
        └── emhttp/
            └── plugins/
                └── myplugin/
                    β”œβ”€β”€ myplugin.page
                    β”œβ”€β”€ default.cfg
                    └── [other files]

Additional files can go elsewhere:

[package root]/
β”œβ”€β”€ usr/
β”‚   β”œβ”€β”€ local/
β”‚   β”‚   β”œβ”€β”€ emhttp/
β”‚   β”‚   β”‚   └── plugins/
β”‚   β”‚   β”‚       └── myplugin/
β”‚   β”‚   └── bin/
β”‚   β”‚       └── mytool           # CLI utility
β”‚   └── lib/
β”‚       └── docker/
β”‚           └── cli-plugins/
β”‚               └── docker-myplugin  # Docker CLI plugin
└── install/
    └── slack-desc               # Package description

Best Practices

1. Minimize USB Writes

The USB flash has limited write cycles:

# Bad - writes on every call
echo "$value" > /boot/config/plugins/myplugin/counter.txt

# Good - write to RAM, sync occasionally
echo "$value" > /tmp/myplugin/counter.txt
# Only sync to flash on shutdown or periodically

Link from flash to RAM for frequently accessed data:

# During install script
if [ ! -L /boot/config/plugins/myplugin/logs ]; then
    mkdir -p /tmp/myplugin/logs
    ln -s /tmp/myplugin/logs /boot/config/plugins/myplugin/logs
fi

3. Handle Missing Directories

Files on flash may not exist on first install:

#!/bin/bash
# Create directories if needed
mkdir -p /boot/config/plugins/myplugin

# Source config with fallback
if [ -f /boot/config/plugins/myplugin/myplugin.cfg ]; then
    source /boot/config/plugins/myplugin/myplugin.cfg
fi

4. Clean Up on Remove

In your remove script:

# Remove plugin files
rm -rf /boot/config/plugins/myplugin

# Remove runtime files
rm -rf /tmp/myplugin
rm -f /var/run/myplugin.pid

# Remove the package
removepkg myplugin-package

5. Document Data Locations

In your README, tell users where data is stored:

## Data Locations

- **Configuration**: `/boot/config/plugins/myplugin/myplugin.cfg`
- **Application Data**: `/mnt/user/appdata/myplugin/`
- **Logs**: Check syslog with `grep myplugin /var/log/syslog`

Troubleshooting

Files Missing After Reboot

Problem: Custom files in /usr/local/emhttp/plugins/ disappear after reboot.

Solution: Files must be in your .txz package or created by an install script.

Config Not Saving

Problem: Settings don’t persist.

Solution: Verify the form writes to /boot/config/plugins/yourplugin/:

<input type="hidden" name="#file" value="myplugin/myplugin.cfg">

Out of Space on Flash

Problem: USB flash drive is full.

Solution:

  1. Clean /boot/config/plugins-removed/
  2. Clean /boot/config/plugins-stale/
  3. Move large data to array shares
  4. Remove cached packages you don’t need

Next Steps