When managing multi-container applications on a Linux VPS, directly using scattered docker run commands can easily cause forgotten parameters, confused network topology, and disastrous upgrades. Modern container delivery uses Docker Compose to define image tags, environment variables, persistent volumes, networking, and resource limits as Infrastructure as Code.
Focused on high availability and data security in production operations, this article explains standardized Compose directory organization, internal network isolation, tighter permissions for graphical management panels, and smooth update and emergency rollback procedures that account for database migrations. It also uses the console capabilities of major cloud providers such as BandwagonHost and DMIT to provide guidance on low-level troubleshooting and operational disaster recovery.
1. Architecture Standards: One Application Directory and Isolated State#
To ensure portability and cold migration in seconds, host-level container orchestration should follow the principle of one directory per application stack. It is recommended to standardize on /srv/stacks/<应用名> or /opt/stacks/<应用名> as the root path for isolation.
/opt/stacks/web-app/
├── compose.yaml # 核心编排文件(Compose V2 规范)
├── .env # 敏感环境变量(权限 600,禁止提交至公开仓库)
├── config/ # 挂载至容器的应用专属静态配置文件
│ └── nginx.conf
└── data/ # 宿主机绑定挂载的数据目录(非必要时优先使用命名卷)
└── app-uploads/Directory and Credential Management Guidelines#
- Updated Naming Conventions:现代 Compose 插件统一采用
compose.yaml(orcompose.yml) as the default configuration file; the old top-levelversion: '3.8'声明行。调用命令统一为docker compose(with a space between the words), rather than the obsolete standalone binarydocker-compose。 - 环境凭据隔离: Dynamic configuration values such as database passwords and API Tokens must be extracted into the same directory's
.envfile, with Compose automatically performing variable interpolation (${VARIABLE_NAME}). To protect the host, this file's permissions must be locked down:chmod 600 /opt/stacks/web-app/.env chown -R root:root /opt/stacks/web-app - Version Control Boundaries:若使用 Git 追踪运维配置,应在仓库内提供
.env.example模板,并在.gitignore中严格排除.envand all that contain actual business statedata/目录。
2. Production-Grade Orchestration Standards and Network Isolation#
directly map all container ports to the host's 0.0.0.0 is a serious network security risk. The Docker daemon directly modifies the system's iptables rules and inject DOCKER chain,Bypasses the System's UFW or firewalld Settings. Once a database or cache service is mapped externally, its port is directly exposed to the public internet.
以下是一套集成独立应用 Networking 、日志滚动上限、 resources 约束与反向代理预留的 Standard compose.yaml Template:
services:
web:
image: ghost:5-alpine
container_name: production_blog_web
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
database__client: mysql
database__connection__host: db
database__connection__user: ghost
database__connection__password: ${MYSQL_PASSWORD}
database__connection__database: ghost_prod
url: https://example.com
volumes:
- blog_content:/var/lib/ghost/content
ports:
# 严格限制仅监听宿主机本地环回接口,交由外部 Nginx 或 Caddy 处理 TLS 与反代
- "127.0.0.1:2368:2368"
networks:
- frontend_net
- backend_net
deploy:
resources:
limits:
cpus: '1.50'
memory: 1024M
logging:
driver: json-file
options:
max-size: "20m"
max-file: "3"
db:
image: mariadb:10.11
container_name: production_blog_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ghost_prod
MYSQL_USER: ghost
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- db_data:/var/lib/mysql
# 绝不发布 ports,仅在内部网络被同栈应用发现
networks:
- backend_net
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "2"
volumes:
blog_content:
name: blog_app_content
db_data:
name: blog_app_db_data
networks:
frontend_net:
driver: bridge
backend_net:
driver: bridgeKey Architectural Considerations#
- Two-Layer Network Isolation:
dbThe Container Joins Onlybackend_net, completely eliminating the possibility of communication with the public internet;webContainers use Compose's built-in DNS and the service namedb解析并建立连接。 - Design to Prevent Disk Exhaustion: Docker's default logging driver allows container stdout/stderr logs to grow without limit. Explicitly declare in the configuration
max-sizeandmax-filecan enforce log rotation and prevent low-specification VPS instances from going down because their disks fill up. - Pin Image Versions: Never use the following directly in production:
:latest. Use stable images with explicit major versions or pinned build hashes (such asmariadb:10.11、alpinebranch) to avoid pulling a build with breaking changes during an unexpected restart.
3. Deployment Verification and Common Command Workflows#
After setting up the directory structure, follow the standard sequence: “syntax check $\to$ image pull $\to$ background build $\to$ status audit”:
# 1. 验证配置文件合法性与变量解析状态(不显示敏感数据)
docker compose config --quiet
# 2. 预先抓取所有编排中的镜像,降低上线中断时长
docker compose pull
# 3. 后台守护模式创建并启动容器
docker compose up -d
# 4. 检查服务健康状态与端口监听
docker compose ps
# 5. 持续观测业务输出以排查初始化异常
docker compose logs -f --tail=100 webWhen Troubleshooting, Remember:docker compose config will .env all internal variables are fully expanded. When sharing debugging information with your team or a support ticket system, never paste the complete unsanitized configuration.
4. Choosing Visual Panels (Portainer / 1Panel) and Reducing Their Attack Surface#
Some teams add lightweight container management panels to simplify collaboration or monitoring. However, it is essential to recognize that:All Management Panels Connected to Docker Are Essentially Privileged Processes。
Docker Socket Security Boundaries#
Nearly all visual management panels, such as Portainer, need to mount the host's Docker communication socket:
-v /var/run/docker.sock:/var/run/docker.sock
Mounting This Socket into a Container Grants It the Same Host-Level root 等同的控制权限。一旦面板的前端密码外泄或存在远程代码执行漏洞,攻击者可以直接 Passed 该套接字创建特权容器挂载宿主机根目录,直接接管整个系统。
Panel Hardening Guidelines#
- Do Not Expose to the Public Internet:
切勿将面板端口(例如 Portainer 的 9443 或 1Panel 端口)直接开放至 public network 。生产环境下应强制绑定为本地监听(
127.0.0.1), with access provided only through local port forwarding over a secure SSH-key-based tunnel:Then open in your local browser# 在本地工作电脑建立安全隧道,将远程端口转发至本地 ssh -N -L 9443:127.0.0.1:9443 user@vps-ip -i ~/.ssh/id_ed25519https://127.0.0.1:9443access the panel. - Avoid Configuration Drift from Two-Way Management:
State conflicts can easily arise between a graphical panel and local YAML files. If you change a container's mapped ports or environment variables directly in the panel's WebUI while the local
compose.yaml未作同步,当下一次 Passed 终端执行docker compose up -dany temporary changes made in the panel will be completely erased. Best Practices: Use the panel only as a Read-only monitor or log-audit tool. Keep the version-control repository and terminal scripts as the Single Source of Truth for all create, read, update, and delete operations.
5. Graceful Image Updates, Data Migration, and Rollback Control#
Many beginners assume that updating a container only means pulling the latest image and restarting it. For persistent business applications, this can easily interrupt database Schema migrations or corrupt data.
Standard Four-Step Rolling Update Method#
[1. 业务静态化与状态备份] ──> [2. 拉取新镜像] ──> [3. 差异化重建容器] ──> [4. 健康校验]Step One: Perform Cold Backups of the Application and Database#
Before switching to a new image, create a logical dump of the data rather than relying solely on a host disk snapshot.
# 进入部署目录
cd /opt/stacks/web-app
# 逻辑导出当前数据库数据并压缩
docker compose exec db mariadb-dump -u ghost -p"${MYSQL_PASSWORD}" ghost_prod | gzip > backup_$(date +%F_%H%M).sql.gz
# 备份持久化配置与上传目录
tar -czvf app_data_$(date +%F).tar.gz config/ data/Step Two: Update Images and Replace Containers#
修改 compose.yaml the target service (such as web) version tag, then perform a differential rebuild:
# 仅拉取更新的镜像层
docker compose pull web
# 触发依赖项计算,仅重新创建发生了配置或镜像变更的容器
docker compose up -d web
# 清理不再使用的孤儿容器与旧版本镜像
docker compose ps
docker image prune -fdocker compose up -d can update the stack in place without disrupting a separate database service that remains healthy.
The Fatal Rollback Misconception: The Schema Trap#
When a new service version fails to start correctly and an urgent rollback is needed, many people simply change compose.yaml change the image version back to the old tag there and run up -d。
Critical Hidden Risk: many web applications, such as Nextcloud, WordPress, and Ghost, automatically perform irreversible database schema migrations when a new version first starts, including dropping columns, renaming fields, or converting character sets. Older application code cannot interpret tables modified by the newer version, so errors and crashes continue even if the image rollback succeeds.
Complete Rollback Procedure:
- Stop the Application Containers Immediately:
docker compose stop web - 使用先前导出的备份 Reset Databases :
# 清空并恢复数据库 gunzip < backup_YYYY-MM-DD.sql.gz | docker compose exec -T db mariadb -u ghost -p"${MYSQL_PASSWORD}" ghost_prod - 修改
compose.yaml恢复旧版 image 标签,并恢复对应的旧版配置文件。 - Rebuild the Previous Container Version:
docker compose up -d web
Warning: Avoid
docker compose down -vdocker compose downonly stops and removes containers and bridge networks; however, if it includes-v(or--volumes) parameter, Docker willerase directlycompose.yamlNamed Volumes declared in. Unless you intend to destroy the environment completely, never use the following option on a production server:-vcommand.
6. Coordinating Underlying Cloud Networking and Emergency Operations#
Container orchestration runs on the underlying cloud server. Network interface problems, firewall blocks, or lost credentials can directly disrupt container lifecycles. Making effective use of the host's infrastructure features is the final line of defense against operational disasters.
Troubleshooting Firewall and Underlying Connectivity Failures#
Because Docker manages NAT tables and bridges itself (docker0 / br-xxxx), routing-table conflicts may occasionally occur after containers restart, even making the host's SSH daemon unreachable.
- DMIT Emergency Access: DMIT's instance control panel provides a dedicated Web Console 入口。当 SSH 端口因 Docker 容器端口占用或 Networking 策略封锁无法连入时,应直接 Passed 面板 Console 发送硬件中断信号或直接登录排查。此外,DMIT 系统为了防范暴力破解,默认关闭了远程
rootpassword login, strongly favoring SSH-key access; if you need to replace or inject public keys in bulk through the panel's Access menu, after the operationRestart the Instance (Reboot) as Instructed by the Panel, after which the underlying cloud-init and authentication mechanisms take effect. - In-Depth BandwagonHost KiwiVM Troubleshooting: BandwagonHost assigns each VPS a separate KiwiVM panel. Note that the KiwiVM panel password, client area account password, and the system's internal
root密码完全独立。当宿主机 Networking 发生故障时,可使用 KiwiVM 内置的 Interactive Console Bypass the regular network stack to access a TTY terminal directly, then enterrootpassword to take control of the server. If incorrect container networking disables the entire network, the panel can even initiate an OS reinstall or snapshot restore.
自动化续费与数据保全边界#
Container orchestration allows applications to be rebuilt quickly, but does not replace cross-node disaster recovery for persistent data. If an invoice becomes overdue or data center policies change, all data volumes on the host may be erased directly.
- Billing and Retention Windows:
- BandwagonHost: The provider does not automatically charge a linked credit card or PayPal account without your involvement. If renewal is enabled, an invoice is generated 7 days before expiration. If the prepaid account balance is sufficient, the system deducts it to renew the service; otherwise, an administrator must complete checkout manually.
- DMIT: Monthly renewal invoices are also generated 7 days before expiration. If late payment causes suspension, the data center usually provides only 3 daysof data retention grace time; once it expires, automated processes permanently destroy the instance and its mounted Docker data, with no recovery.
- Cross-Data-Center Migration Restrictions: although BandwagonHost offers convenient cross-region data center migration, if prohibited outbound activity or abuse causes an instance's IP to be blacklisted, the system strictly restricts migration in the underlying panel. If a misconfigured container is compromised and used to scan external networks, consequences range from IP blocking and service disruption to loss of migration and recovery privileges.
- Minimum Off-Site Disaster Recovery Requirements: Regardless of the underlying provider, persistent directories (Volumes) and database Dump backups must be encrypted and uploaded outside the host on a daily schedule using object storage clients, such as AWS S3, R2, or Backblaze B2. Never keep all your eggs in one instance's virtual disk.
7. Production Handover Checklist#
After any Docker Compose deployment or major-version update, complete the handover audit using the following steps:
| Validation Category | Key Checks | Verification Method / Expected Behavior |
|---|---|---|
| Network Isolation | Database/Middleware Ports Are Not Exposed to the Public Internet | Run on the Host ss -tulpn | grep -E '3306|5432|6379' No External Listener |
| Privilege Review | 业务容器禁止使用特权模式 | compose.yaml 中无 privileged: true, with no unnecessary host mounts |
| resources 限制 | Memory and CPU Hard Limits Are Configured and Effective | After startup, use docker stats --no-stream Verify That Container Memory Limits Have Taken Effect |
| Log Rotation | 日志不会撑爆宿主机磁盘 | Check that every service declares max-size and max-file Restrictions |
| Automatic Startup After a Power Outage | Automatic Recovery After a Host Reboot | Service Configuration restart: unless-stopped, after restarting docker ps Shows Up |
| 备份离线 | 异地备份策略与备份校验 | Verify That Automated SQL Backup Files Are Nonempty and Can Be Imported into a Test Environment |
| Emergency Access Channel | Operations Console Available at All Times | Verify the Provider's Console Login Credentials in Advance to Ensure Low-Level Troubleshooting Access During a Network Outage |