Docker Development
Overview
Docker applications run in containers managed by the TOS 7 Docker Engine.
Prerequisite: Docker Engine is not pre-installed in TOS 7. It is provided as an application in the TOS App Center and must be installed and enabled by the user. When users install a Docker-based application, the platform automatically checks for Docker Engine and prompts installation if it is missing or disabled — no additional action is required from the developer.
Core Requirements:
- Must provide a
docker-compose.ymlcompatible with Compose Spec 3.8+ - Data must be persisted to NAS-accessible directories via volume mounts
- Privileged mode is strictly prohibited
- System core ports (22, 80, 443, 8181, 5050) must not be occupied
Package Structure (.tar.gz Archive)
The Docker application is submitted as a .tar.gz archive. The archive must contain exactly the following files at the root level:
<appid>.tar.gz
├── config.ini
├── <appid>.lang
├── <appid>.svg
└── docker-compose.yml
File descriptions:
| File | Required | Description |
|---|---|---|
config.ini | ✅ Yes | Application metadata configuration |
<appid>.lang | ✅ Yes | Multilingual file (14 languages) |
<appid>.svg | ✅ Yes | Application icon (SVG format) |
docker-compose.yml | ✅ Yes | Container orchestration configuration |
Important Notes:
- The
config.ini.iconfield must point to/images/icons/<appid>.svg. The platform handles the mapping during installation.- The package name follows the format defined in Chapter 4 · Section 3:
<appid>.tar.gz- For Docker applications with a UI, the
docker-compose.ymlmust include thex-app-metasection (see Section 3).- For Docker applications without a UI, the
x-app-metasection is not required.
docker-compose.yml Specification
version: "3.8"
services:
<appid>:
image: <registry>/<image>:<tag> # Images limited to Docker Hub only
container_name: <appid>
restart: unless-stopped
Volumes:
- /Volume*/DockerAppData/<appid>/config:/config
- /Volume*/DockerAppData/<appid>/data:/data
ports:
- "<host_port>:<container_port>"
environment:
- TZ=Asia/Shanghai
user: "1000:1000"
x-app-meta:
web:
port: <host_port>
protocol: http
Note:
*is documentation notation only for the volume number (e.g., Volume1, Volume2) chosen at installation. The platform does not expand it — never write/Volume*/…into a compose file, a systemd unit, a lifecycle script, or any other machine-read configuration.
Rules:
-
Version: Must be compatible with Compose Spec 3.8 or higher
-
x-app-meta: For Docker applications with a UI, the
x-app-metatag must be a top-level key in thedocker-compose.ymlfile, containingweb.port(Web UI port number) andweb.protocol(request protocol, typicallyhttp).x-app-meta:
web:
port: 8080
protocol: http -
Comments: You can add comments in the
docker-compose.ymlfile. In YAML, comments start with#and must be preceded by a space (e.g.,port: 8080 # Web UI port). Comments may also appear on their own line. -
Data Persistence: All data directories must be mounted to host paths. Data stored only inside the container will be lost when the container is removed.
Host-side volume path syntax (supported forms): The platform does not expand
/Volume*/. Write the host side of every bind mount in one of the forms below; the data is placed under the application data root/Volume<N>/DockerAppData/<appid>/.Compose syntax Type Host-side meaning Data location ./data:/dataBind Mount Host relative path /Volume1/DockerAppData/<appid>/data/data:/dataBind Mount Host absolute path /Volume1/DockerAppData/<appid>/data/Volume1/app/data:/dataBind Mount Host absolute path /Volume1/DockerAppData/<appid>/dataapp-data:/dataNamed Volume Docker volume name — /dataAnonymous Volume No source; Docker creates it — type: tmpfstmpfs In-memory filesystem — ⛔ Never write
/Volume*/…in a compose file, a systemd unit, a lifecycle script, or any other machine-read configuration. The platform does not resolve*: the literal directory/Volume*is created, and your data is written there instead of the application data root.Relative-path base: in
./data:/data, the.is resolved against the directory that contains thedocker-compose.ymlfile — the standard Compose behaviour, where relative host paths are resolved against the Compose project directory. The platform then places the resolved directory under the application data root, so the same compose file works no matter which volume the user picks at installation. -
Port Mapping:
- Disabled ports: 22, 80, 443, 8181, 5050 (system services)
- Recommended range: 8000-19999
- Verify that the selected port is not in use on the TNAS before submission
-
Privileged Mode: Strictly prohibited. The
userfield must be used to specify UID/GID. -
Timezone: Default configuration
TZ=Asia/Shanghai. Users may modify as needed. Do not leave the timezone empty — inconsistent timestamps can cause data corruption in time-sensitive applications. -
Container Name: You are free to set a custom name, provided it is globally unique on the Docker daemon. Duplicate names will result in runtime errors.
-
Restart Policy: Use
unless-stoppedfor normal services -
Network Mode:
network_mode: hostis strictly prohibited, except for system-level network tools. System-level network tools must clearly state the rationale at submission and may only use it after approval. Regular applications are strictly prohibited. Using host network mode breaks container isolation and poses security risks. Use port mapping instead:
ports:
- "8080:8080"
- Shared Folders: Docker applications do not participate in the TNAS shared-folder mechanism. Unlike Deb applications, no shared folder is created for a Docker application, it must not call
ter_share_addor declareshare_folders, and shared-folder mount placeholders (<shared_folder_path>) must not appear indocker-compose.yml. All persistent business data stays inside the container and must be stored through volume mounts, which the platform resolves to the application data root/Volume<N>/DockerAppData/<appid>/.
Image and Security Requirements
-
Image Source (Docker Hub Only): All Docker images must come from Docker Hub. Non-Docker Hub images will be rejected outright. Images must be hosted on Docker Hub (hub.docker.com). Other image registries (such as ghcr.io, quay.io, self-hosted private registries, etc.) are currently not supported.
Priority Source Example 1 (Preferred) Docker Hub official project images nginx,postgres2 Docker Hub verified publishers Docker Hub images with Verified badge 3 Docker Hub well-known community images linuxserver/jellyfin(100M+ pulls, actively maintained)❌ Rejected Images from non-Docker Hub sources Private registries, ghcr.io, quay.io, etc. ❌ Rejected Unverified personal images on Docker Hub Docker Hub images with few pulls, no documentation Mandatory Requirement: Images must be hosted on Docker Hub. Image source will be verified during review. Using non-Docker Hub images will result in immediate rejection.
Images from non-Docker Hub sources or unverified Docker Hub images will be rejected during security review.
-
Image Size: Use multi-stage builds or Alpine base images to reduce size.
-
Sensitive Information: Hardcoding passwords, tokens, or secrets in images or compose files is prohibited. Use environment variables or
.envfiles. -
Security Scanning: Run
docker scanortrivybefore submission to check for known vulnerabilities. -
User Permissions: Running as root is strictly prohibited, and
--privilegedmode is strictly prohibited. A non-root user must be specified via theuserfield.
Complete Example
Application Overview:
- ID:
myapp-docker - Type: Docker application
- Image:
linuxserver/myapp:latest - Port: 8080
- Dependency: DockerEngine
config.ini
{
"id": "myapp-docker",
"icon": "/images/icons/myapp-docker.svg",
"publisher": "Developer Name",
"path": "http://${ip}:8080",
"exec": true,
"open_path": true,
"resize": true,
"maxmin": true,
"width": 0,
"height": 0,
"help": "https://github.com/example/myapp/wiki",
"version": "1.0.0",
"recommend": false,
"beta": false,
"low_version": "TOS7.0",
"category": ["Utilities"],
"depend": ["DockerEngine"],
"relation": ["docker", "DockerEngine"],
"platform": "x86_64",
"official": "https://example.com",
"application_type": "docker",
"system_id": "",
"package": "",
"compose_project": "myapp-docker",
"user": "myapp",
"all_user_display": true,
"allow_open_in_mobile": false
}
docker-compose.yml
version: "3.8"
services:
myapp-docker:
image: linuxserver/myapp:1.0.0
container_name: myapp-docker
restart: unless-stopped
Volumes:
- /Volume*/DockerAppData/myapp-docker/config:/config
- /Volume*/DockerAppData/myapp-docker/data:/data
ports:
- "8080:8080"
environment:
- TZ=Asia/Shanghai
- PUID=1000
- PGID=1000
x-app-meta:
web:
port: 8080
protocol: http
Note:
*is documentation notation only for the volume number (e.g., Volume1, Volume2) chosen at installation. The platform does not expand it — never write/Volume*/…into a compose file, a systemd unit, a lifecycle script, or any other machine-read configuration. This application opens its WebUI externally, sopathuses thehttp://${ip}:<port>format.
Multi-container Service Startup Order: For applications with multiple services (e.g., Web + Database):
services:
app-db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
app-web:
image: myapp:1.0.0
depends_on:
app-db:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
- Use
depends_onwithcondition: service_healthyto ensure correct startup order - Health checks must be defined for every service
- Platform validation: all services must be healthy before the application is shown as "Running"
Health Check Failure Handling:
- After 3 consecutive health check failures, the container is marked as
unhealthy. - The Application Center will change the application status to "Not Enabled", and users need to manually click to enable it.
- TOS 7 does not have a built-in Docker restart policy; if needed, developers must configure the required restart policy in
docker-compose.ymlthemselves. - If the container encounters runtime errors, users can view the error logs through the Container Manager application.
Data Backup, Migration, and Reset
| Operation | Method | Notes |
|---|---|---|
| Backup | Copy the entire /Volume*/DockerAppData/<appid>/ directory to a backup location | It is recommended to back up the configuration and data directories before upgrading |
| Migration | Copy the data directory to a new volume and update the volume mount paths in docker-compose.yml | Cross-volume migration is supported; ensure the container is stopped before proceeding |
| Reset | Delete the /Volume*/DockerAppData/<appid>/data/ and /Volume*/DockerAppData/<appid>/config/ directories | Resets to initial state; user data in shared folders is not affected |
Note: Configuration and data are stored separately, supporting independent backup and recovery. Major upgrades require backing up both directories.