0

Advanced Play with Docker Compose in NAS

Share
  • November 2, 2025

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
servicesDefines 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
volumesDefine Named Volumes. These volumes can be shared between different services and are independent of the container lifecycle.selectable
networksDefine custom networks. Allows services to connect to a specific network in order to communicate with each other or with an external network.selectable
configsDefine Config Data. Allows a service to access sensitive or non-sensitive configuration data, typically used for application configuration.selectable
secretsDefine Sensitive Data (Secrets). Allows services to securely access sensitive information such as passwords or API keys.selectable
includeContains additional Compose files. Allows configuration to be split into multiple files for easy management and reuse.selectable
nameSpecifies 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.

imageSpecifies the image used to build the container. This can be a public image on Docker Hub, a private repository or a local image.
buildDefines how to build the image. Use this to specify the Dockerfile path and build context if not using an existing image.
container_nameSpecify a fixed name for the container. By default Compose generates a name (e.g. projectname-servicename-1 ).
portsPublish the container’s port to the host. The format is usually Host Port:Container Port.
volumesMount 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.
environmentSets an environment variable in the container. This can be a list of key-value pairs or file references.
env_fileAdd environment variables from the file.
networksSpecifies which networks the service should connect to.
depends_onDefine dependencies between services. Ensure that dependent services are started before the current service is started.
restartDefines the restart policy when the container exits. Examples are always, on-failure, unless-stopped.
commandOverrides the default commands executed when the container starts.
entrypointOverrides the default entry point of the container.
labelsAdd custom metadata (tags) to the container.
healthcheckDefine a command to check the health status of a container.
loggingConfigure the logging driver and options for the service.
deploySpecify 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.