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 Type | Path | Description |
|---|---|---|
| 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:
- Runtime data is stored in
/Volume*/@apps/<appid>/data/ - 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> - To maintain compatibility, the application can create symbolic links:
ln -s /Volume*/<appid> /Volume*/@apps/<appid>/data - The shared folder
/Volume*/<appid>/is accessible to users via SMB/NFS
Docker Applications:
- Mount configuration and runtime data to
/Volume*/DockerAppData/<appid>/:Volumes:
- /Volume*/DockerAppData/<appid>/config:/config
- /Volume*/DockerAppData/<appid>/cache:/cache - User data must be stored in a shared folder:
Volumes:
- /Volume*/<appid>:/data - Storing data in the container filesystem is prohibited
- 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 Type | Maximum Retention | Cleanup Method |
|---|---|---|
| Application Logs (files) | 30 days | Logrotate: daily rotation, retain 30 files |
| Systemd Journal | Managed by platform | Automatically managed via journald limits |
| Docker Container Logs | 10MB per file, 3 files total | Docker logging driver configuration |
Logrotate Configuration:
/Volume*/@apps/<appid>/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
}
Resource Limits
| Resource | Deb Applications (systemd) | Docker Applications (compose) |
|---|---|---|
| Memory | MemoryMax=512M | memory: 512M |
| CPU | CPUQuota=200% | cpus: '2.0' |
| File Descriptors | LimitNOFILE=65536 | N/A (container level) |
| Processes | LimitNPROC=256 | N/A (container level) |
| Disk | N/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:
- Always check for old versions in
postinst:if [ -n "$2" ]; then
# Upgrading from $2 — run migration
/Volume*/@apps/<appid>/bin/migrate --from "$2"
fi - Never delete user data during upgrades
- Back up before modifying configuration formats
- Migration logic should be reversible to support rollback
- Users are advised to store data within
/Volume*/@apps/<appid>/data/or within/Volume*/to ensure data is not lost after upgrades
Docker Applications:
- 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 - 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=trueProtectSystem=strictProtectHome=trueReadWritePaths(explicit paths only)- Non-root
User/GroupRecommended (strongly suggested):
AmbientCapabilities(only needed capabilities)LimitNOFILE,LimitNPROCPrivateTmp=truePrivateDevices=trueOptional (advanced hardening):
PrivateNetwork=true(only when network is not needed)PrivateUsers=trueMemoryDenyWriteExecute=true
Application Port Allocation
Rules:
- Prioritize selecting ports within the recommended range 8000-19999 (12,000 ports total, greatly reducing conflict probability)
- If the recommended range ports are occupied, 49152-65535 (dynamic port range) can be used as an alternative.
- Check commonly used ports to avoid conflicts before selection; make ports configurable via environment variables
- 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):
| Port | Application |
|---|---|
| 22 | SSH |
| 80 | TOS Web (HTTP) |
| 443 | TOS Web (HTTPS) |
| 445 | SMB |
| 3306 | MySQL |
| 5050 | TOS Daemon |
| 5432 | PostgreSQL |
| 6379 | Redis |
| 8096 | Jellyfin |
| 8181 | TOS Nginx |
| 8443 | TOS HTTPS |
| 9000 | Portainer |
| 9090 | Prometheus |