Lewati ke konten utama
Version: TOS 7

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.yml compatible 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:

FileRequiredDescription
config.ini✅ YesApplication metadata configuration
<appid>.lang✅ YesMultilingual file (14 languages)
<appid>.svg✅ YesApplication icon (SVG format)
docker-compose.yml✅ YesContainer orchestration configuration

Important Notes:

  • The config.ini.icon field 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.yml must include the x-app-meta section (see Section 3).
  • For Docker applications without a UI, the x-app-meta section 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:

  1. Version: Must be compatible with Compose Spec 3.8 or higher

  2. x-app-meta: For Docker applications with a UI, the x-app-meta tag must be a top-level key in the docker-compose.yml file, containing web.port (Web UI port number) and web.protocol (request protocol, typically http).

    x-app-meta:
    web:
    port: 8080
    protocol: http
  3. Comments: You can add comments in the docker-compose.yml file. 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.

  4. 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 syntaxTypeHost-side meaningData location
    ./data:/dataBind MountHost relative path/Volume1/DockerAppData/<appid>/data
    /data:/dataBind MountHost absolute path/Volume1/DockerAppData/<appid>/data
    /Volume1/app/data:/dataBind MountHost absolute path/Volume1/DockerAppData/<appid>/data
    app-data:/dataNamed VolumeDocker volume name—
    /dataAnonymous VolumeNo source; Docker creates it—
    type: tmpfstmpfsIn-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 the docker-compose.yml file — 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.

  5. 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
  6. Privileged Mode: Strictly prohibited. The user field must be used to specify UID/GID.

  7. 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.

  8. 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.

  9. Restart Policy: Use unless-stopped for normal services

  10. Network Mode: network_mode: host is 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"
  1. 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_add or declare share_folders, and shared-folder mount placeholders (<shared_folder_path>) must not appear in docker-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​

  1. 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.

    PrioritySourceExample
    1 (Preferred)Docker Hub official project imagesnginx, postgres
    2Docker Hub verified publishersDocker Hub images with Verified badge
    3Docker Hub well-known community imageslinuxserver/jellyfin (100M+ pulls, actively maintained)
    ❌ RejectedImages from non-Docker Hub sourcesPrivate registries, ghcr.io, quay.io, etc.
    ❌ RejectedUnverified personal images on Docker HubDocker 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.

  2. Image Size: Use multi-stage builds or Alpine base images to reduce size.

  3. Sensitive Information: Hardcoding passwords, tokens, or secrets in images or compose files is prohibited. Use environment variables or .env files.

  4. Security Scanning: Run docker scan or trivy before submission to check for known vulnerabilities.

  5. User Permissions: Running as root is strictly prohibited, and --privileged mode is strictly prohibited. A non-root user must be specified via the user field.

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, so path uses the http://${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_on with condition: service_healthy to 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.yml themselves.
  • If the container encounters runtime errors, users can view the error logs through the Container Manager application.

Data Backup, Migration, and Reset​

OperationMethodNotes
BackupCopy the entire /Volume*/DockerAppData/<appid>/ directory to a backup locationIt is recommended to back up the configuration and data directories before upgrading
MigrationCopy the data directory to a new volume and update the volume mount paths in docker-compose.ymlCross-volume migration is supported; ensure the container is stopped before proceeding
ResetDelete the /Volume*/DockerAppData/<appid>/data/ and /Volume*/DockerAppData/<appid>/config/ directoriesResets 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.