Those of you who are familiar with me know that I often toss NAS, and I also belong to the NAS deployment of many self-hosted services, which Docker is essential. Different NAS systems have different solutions for Docker, and Docker Compose is currently the most widely applicable one.
Labeling instructions
The Docker Compose configuration file is in YAML format, usually a docker-compose.yml or compose.yaml file. The top-level tags define the configuration of services, networks, storage volumes, etc. for the entire application. The following is a list of common and important top-level tag names and their functions in the Docker Compose configuration file. This list is based on the Compose Specification, which replaces the older v2 and v3 formats and is the recommended and widely used standard at this stage.
version (deprecated) | (no longer recommended in newer Compose specifications) Used in older Compose file formats (v2 and v3) to specify the profile version. When using the Compose specification now, this field is usually omitted or used only for backward compatibility. | old version |
services | Defines all services in the application. Each service entry (such as image, container_name, etc. as you mentioned) acts as a subconfiguration under this key. This is the core part of the Compose file. | compulsory |
volumes | Define Named Volumes. These volumes can be shared between different services and are independent of the container lifecycle. | selectable |
networks | Define custom networks. Allows services to connect to a specific network in order to communicate with each other or with an external network. | selectable |
configs | Define Config Data. Allows a service to access sensitive or non-sensitive configuration data, typically used for application configuration. | selectable |
secrets | Define Sensitive Data (Secrets). Allows services to securely access sensitive information such as passwords or API keys. | selectable |
include | Contains additional Compose files. Allows configuration to be split into multiple files for easy management and reuse. | selectable |
name | Specifies the project name. Used to override the default behavior of directory names as project names. | selectable |
Of these top-level tags, the services tag is essential for building containers, and there are a number of common sub-tags under it.
image | Specifies the image used to build the container. This can be a public image on Docker Hub, a private repository or a local image. |
build | Defines how to build the image. Use this to specify the Dockerfile path and build context if not using an existing image. |
container_name | Specify a fixed name for the container. By default Compose generates a name (e.g. projectname-servicename-1 ). |
ports | Publish the container’s port to the host. The format is usually Host Port:Container Port. |
volumes | Mount storage volumes or host paths to containers. Used for data persistence or shared host files. The format can be host path:container path or volume name:container path. |
environment | Sets an environment variable in the container. This can be a list of key-value pairs or file references. |
env_file | Add environment variables from the file. |
networks | Specifies which networks the service should connect to. |
depends_on | Define dependencies between services. Ensure that dependent services are started before the current service is started. |
restart | Defines the restart policy when the container exits. Examples are always, on-failure, unless-stopped. |
command | Overrides the default commands executed when the container starts. |
entrypoint | Overrides the default entry point of the container. |
labels | Add custom metadata (tags) to the container. |
healthcheck | Define a command to check the health status of a container. |
logging | Configure the logging driver and options for the service. |
deploy | Specify deployment and runtime constraints (mainly used in Swarm mode). |
example
To make it easier to understand, I’ll use two samples to illustrate. I’ll add comments (notes) using the # symbol next to each tag.
The following example shows a scenario where a standalone web server container (using an Nginx image) is configured through a single container.
# Top-level configuration keys (tags) for the Compose file
# The 'version' key is generally discouraged in favor of the latest Compose Specification.
# If needed for backward compatibility, it can be added as:
# version: '3.8'
# Core tag: Defines all services in the application
services:
# Service Name: Can be any name you choose; it acts as a network alias
web_server:
# Service sub-tags:
# Specifies the image used to build the container
image: nginx:latest
# Assigns a fixed name to the container
container_name: my_single_nginx_container
# Mounts volumes or host paths to the container
# Format: [Host Path or Volume Name]:[Container Internal Path]
volumes:
# Mounts the local ./html directory to the Nginx default web directory in the container
- ./html:/usr/share/nginx/html:ro
# Publishes container ports to the host
# Format: [Host Port]:[Container Port]
ports:
# Maps the container's 80 port to the host's 8080 port
- "8080:80"
# Sets environment variables inside the container
environment:
# This is an example environment variable
- TIMEZONE=Asia/Shanghai
# Defines the restart policy when the container exits
# 'unless-stopped' means the container will restart upon exiting, unless manually stopped
restart: unless-stopped
# Adds custom metadata (labels) to the container
labels:
- "com.example.description=Single Nginx Web Server"
# Top-level tag: Defines named volumes (if needed)
# Since this example uses host mounting, the 'volumes' tag can be omitted.
# volumes:
# my_data:
# Top-level tag: Defines custom networks (if needed)
# In this single container example, Compose will use the default bridge network.
# networks:
# default:
# driver: bridge
This example configures a web application (using the Node.js example) and a database (using PostgreSQL) with multiple containers and configures the dependencies and communication between them.
# Top-level configuration keys (tags) for the Compose file
# Core tag: Defines all services in the application
services:
# Service One: Web Application
app_service:
# Service sub-tags:
# Specifies how to build the image (building from a Dockerfile)
# 'context' specifies the directory containing the Dockerfile, 'dockerfile' specifies the Dockerfile name
build:
context: .
dockerfile: Dockerfile.app
# Assigns a fixed name to the container
container_name: web_app_container
# Publishes container ports to the host
# Maps the container's 3000 port to the host's 3000 port
ports:
- "3000:3000"
# Sets environment variables inside the container
environment:
# Environment variables needed to connect to the database
- DB_HOST=db_service # Use the service name as the hostname for connection
- DB_PORT=5432
- DB_USER=user
- DB_PASSWORD=secret
# Defines dependencies between services
# Ensures the db_service container is started and running before starting app_service
depends_on:
db_service:
condition: service_started
# Specifies which networks the service should connect to
networks:
- internal_network
- public_network
# Service Two: Database Service
db_service:
# Service sub-tags:
# Specifies the database image to use
image: postgres:14-alpine
# Assigns a fixed name to the container
container_name: postgres_db_container
# Mounts volumes to the container
volumes:
# Uses a named volume for data persistence, ensuring data is not lost after container restarts
- postgres_data:/var/lib/postgresql/data
# Adds environment variables from a file (a safer way to manage secrets)
# Assumes a file named .env.db exists, containing variables like POSTGRES_USER
env_file:
- .env.db
# Defines the restart policy when the container exits
restart: always
# Specifies which networks the service should connect to
# Note: Database services are usually not exposed directly to the public, only connecting to the internal network
networks:
- internal_network
# Top-level tag: Defines named volumes
volumes:
# Defines a volume named 'postgres_data'
postgres_data:
# Can specify driver and options; using default driver here
driver: local
# Top-level tag: Defines custom networks
networks:
# Defines an internally used network for app and db communication
internal_network:
driver: bridge
# Defines a network that might require external connection
public_network:
driver: bridge
Used in NAS
At present, all mainstream NAS systems support Docker Compose, according to their own needs to find the corresponding project Docker Compose, copy and paste into the NAS Docker Compose, and according to their own specific circumstances can be edited.
One special note is to modify the path in the Docker Compose file when using it in your own NAS. As mentioned in the previous section, the mount tag volumes and the variable file env_file tag are both involved in setting the path. Most NAS systems require a path to be set when adding Docker Compose, and this path is where the container project stores its files in the NAS.

In the Docker Compose file, the items under each tab are separated by “:”, the first colon before the first is the mapped host port or address, after the first colon is the port or address within the container, and after the second colon is the setup items such as read/write permissions.
Depending on different developers’ editing code habits, when mapping the mount address, some developers will write the absolute address, while others will write the relative address, using the above sample for reference:
Absolute Address:/volume1/docker/Test/html
Relative Address:./html
In the above example, the html folder is the mapped mount folder, and the ” . / ” means that the html folder is in the /docker/Test directory. Personally, I prefer to use relative paths so that if the container or docker directory is relocated, it doesn’t invalidate the container by losing the directory.
The other is the port, in the example above, it is mapping port 80 in the container to port 8080 on the host, if there are multiple Docker containers running in the NAS, make sure to pay attention to which ports are already occupied, avoid mapping the same port in different containers, which will also lead to invalid containers.