Skip to main content

Best Practices

Application Directory Layout

Follow a consistent directory layout to ensure maintainability and compatibility. All non-embedded applications (both official and third-party) are installed on storage volumes at /Volume*/@apps/<appid>/.

Standard Directory Layout:

/Volume*/@apps/<appid>/
├── <binary> # Application executable
├── config.ini # Application configuration file
├── <appid>.lang # Language file
├── images/ # Icon resources
├── webui.bz2 # Front-end page archive (WebUI applications)
├── nginx/ # Nginx configuration (externally opened applications)
├── init.d/ # Systemd service files
├── data/ # Runtime data (caches, temporary files, writable)
└── logs/ # Application logs

Note: * in /Volume*/ represents the volume number (e.g., Volume1, Volume2) chosen by the user during installation.

Data Storage Recommendations:

  • Runtime data (/Volume*/@apps/<appid>/data/) — Application-generated caches, temporary files, and runtime state. This data can be safely regenerated.
  • Logs (/Volume*/@apps/<appid>/logs/) — Application log files. Ensure log rotation is configured.
  • User business data — Must be stored in shared folders under /Volume*/ (e.g., /Volume*/<appid>/) for user access via SMB/NFS.

Data Persistence

Understanding Data Types:

Data TypePathDescription
Runtime Data/Volume*/@apps/<appid>/data/Caches, temporary files, runtime state (can be regenerated)
User Data/Volume*/<appid>/ (shared folder)Persistent business data (must survive app upgrades)

Deb Applications:

  1. Runtime data is stored in /Volume*/@apps/<appid>/data/
  2. User data must be stored in a shared folder created by the application:
    # In postinst — create a shared folder for user data
    ter_share_add -name <appid> -owner <appid>
  3. To maintain compatibility, the application can create symbolic links:
    ln -s /Volume*/<appid> /Volume*/@apps/<appid>/data
  4. The shared folder /Volume*/<appid>/ is accessible to users via SMB/NFS

Docker Applications:

  1. Mount configuration and runtime data to /Volume*/DockerAppData/<appid>/:
    Volumes:
    - /Volume*/DockerAppData/<appid>/config:/config
    - /Volume*/DockerAppData/<appid>/cache:/cache
  2. User data must be stored in a shared folder:
    Volumes:
    - /Volume*/<appid>:/data
  3. Storing data in the container filesystem is prohibited
  4. Use separate volumes for configuration and data to support independent backups

Note: * in /Volume*/ represents the volume number (e.g., Volume1, Volume2) chosen by the user during installation.

  • Runtime data can be safely deleted without losing user business data
  • User data (shared folder) must be backed up before app upgrades

Logging

Deb Applications:

# Use systemd journal (recommended)
# All stdout/stderr from the service is automatically captured
# View logs: journalctl -u <appid>

# Or write to file
exec >> /Volume*/@apps/<appid>/logs/app.log 2>&1

Docker Applications:

services:
myapp:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

Note: Each container log file is limited to 10MB, with 3 files retained, for a total log size cap of 30MB.

Best Practices:

  • Use structured logging (JSON format recommended)
  • Include timestamp, level, and context in every log entry
  • Rotate logs to prevent disk exhaustion
  • Never log sensitive information (passwords, tokens, personal data)

Log Retention and Cleanup:

Log TypeMaximum RetentionCleanup Method
Application Logs (files)30 daysLogrotate: daily rotation, retain 30 files
Systemd JournalManaged by platformAutomatically managed via journald limits
Docker Container Logs10MB per file, 3 files totalDocker logging driver configuration

Logrotate Configuration:

/Volume*/@apps/<appid>/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}

Resource Limits

ResourceDeb Applications (systemd)Docker Applications (compose)
MemoryMemoryMax=512Mmemory: 512M
CPUCPUQuota=200%cpus: '2.0'
File DescriptorsLimitNOFILE=65536N/A (container level)
ProcessesLimitNPROC=256N/A (container level)
DiskN/A (use quotas)Volume size limit

Guidelines:

  • Set resource limits based on expected workload, not maximum possible usage
  • Reserve a 20-30% peak buffer on top of typical usage
  • Document resource requirements in README.md

Health Checks

Deb Applications:

# In systemd service file
[Service]
StartLimitBurst=3
StartLimitIntervalSec=60

# Watchdog (if application supports it)
WatchdogSec=30

Docker Applications:

services:
myapp:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

Upgrades and Migration

Deb Applications:

  1. Always check for old versions in postinst:
    if [ -n "$2" ]; then
    # Upgrading from $2 — run migration
    /Volume*/@apps/<appid>/bin/migrate --from "$2"
    fi
  2. Never delete user data during upgrades
  3. Back up before modifying configuration formats
  4. Migration logic should be reversible to support rollback
  5. Users are advised to store data within /Volume*/@apps/<appid>/data/ or within /Volume*/ to ensure data is not lost after upgrades

Docker Applications:

  1. Use an entrypoint script to detect and migrate old data formats:
    #!/bin/bash
    if [ -f /config/version ]; then
    OLD_VERSION=$(cat /config/version)
    if [ "$OLD_VERSION" != "$NEW_VERSION" ]; then
    /app/migrate.sh "$OLD_VERSION" "$NEW_VERSION"
    fi
    fi
    echo "$NEW_VERSION" > /config/version
  2. Test upgrade paths for at least the last 2 major versions

Security Hardening

Deb Applications:

[Service]
# Drop all capabilities, add only required ones
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

# File system protection
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/Volume*/@apps/<appid>/data /Volume*/@apps/<appid>/logs

> **Note:** If the application needs to write configuration files under `/etc`, it must use `ReadWritePaths` to explicitly declare writable paths.

# Network namespace (optional)
# PrivateNetwork=true # Only when network is not needed

# User namespace
# PrivateUsers=true

Docker Applications:

services:
myapp:
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # Only when binding to ports below 1024
read_only: true
tmpfs:
- /tmp
- /run

Required (all submissions must include):

  • NoNewPrivileges=true
  • ProtectSystem=strict
  • ProtectHome=true
  • ReadWritePaths (explicit paths only)
  • Non-root User/Group

Recommended (strongly suggested):

  • AmbientCapabilities (only needed capabilities)
  • LimitNOFILE, LimitNPROC
  • PrivateTmp=true
  • PrivateDevices=true

Optional (advanced hardening):

  • PrivateNetwork=true (only when network is not needed)
  • PrivateUsers=true
  • MemoryDenyWriteExecute=true

Application Port Allocation

Rules:

  1. Prioritize selecting ports within the recommended range 8000-19999 (12,000 ports total, greatly reducing conflict probability)
  2. If the recommended range ports are occupied, 49152-65535 (dynamic port range) can be used as an alternative.
  3. Check commonly used ports to avoid conflicts before selection; make ports configurable via environment variables
  4. Document port usage in README.md

Port Range Description:

  • 8000-19999: The recommended port range for TOS 7 applications, avoiding system core service ports (such as 22/80/443/8181), with ample capacity to meet the port needs of the vast majority of applications
  • 49152-65535: IANA-defined dynamic/private port range, suitable for temporary or backup scenarios

Common Port Reference (Avoid Using):

PortApplication
22SSH
80TOS Web (HTTP)
443TOS Web (HTTPS)
445SMB
3306MySQL
5050TOS Daemon
5432PostgreSQL
6379Redis
8096Jellyfin
8181TOS Nginx
8443TOS HTTPS
9000Portainer
9090Prometheus