RAPID INCIDENT TRIAGE & ACTION PLAN•Performance
01. Error Signature
Docker Swarm Unless-Stopped Policy Auditor
02. Root Cause Vector

Docker Swarm silently ignores the 'unless-stopped' restart policy because Swarm mode only supports 'any', 'on-failure', and 'none', causi...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Performance

Docker Swarm Unless-Stopped Policy Auditor

Docker Swarm silently ignores the 'unless-stopped' restart policy because Swarm mode only supports 'any', 'on-failure', and 'none', causing unexpected service restarts or mismatched behavior from Compose migrations.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(55L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
---
# legacy-swarm-deploy.yaml
# TODO: Migrate to K8s after PCI audit Q4 2024
# WARNING: This is a temporary fix for prod-svc-02, do not touch unless authorized by @devops-lead

version: '3.8'

services:
  legacy-api:
    image: my-registry.internal/legacy-api:v2.1.3-stable
    deploy:
      mode: replicated
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 5
        window: 120s
      placement:
        constraints:
          - node.role == manager
    # VULNERABILITY: Swarm mode ignores 'unless-stopped' restart policy; this is silently overridden by the global swarm restart policy.
    restart: unless-stopped  # Override for legacy compatibility (DO NOT REMOVE - breaks health checks)
    environment:
      - DB_URI=postgresql://admin:J8$kLmN9pQrS_tUvWxYz@prod-db.internal.pci-secure-zone:5432/legacy_db
      - AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
      - AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
      - STRIPE_API_KEY=sk_live_4eC39HqLyjWDarjtT1zdp7dc
      - LOG_LEVEL=DEBUG
    volumes:
      - /mnt/legacy-storage:/data:ro
    networks:
      - backend
    # Old config from pre-swarm era (commented but kept for rollback)
    # mem_limit: 512m
    # cpu_shares: 1024

  sidecar-validator:
    image: my-registry.internal/pci-validator:latest
    deploy:
      restart_policy:
        condition: any
    environment:
      - VALIDATOR_KEY=-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArX7LlQ6cN7eJ5Yg5jfH5qK7J8vLm9ZqW... (truncated for brevity)\n-----END RSA PRIVATE KEY-----\n
networks:
  backend:
    driver: overlay
    driver_opts:
      encrypted: "true"

# TODO: Fix volume driver for azure-file (blocked by security team)
volumes:
  legacy-storage:
    driver: local
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

Fixing Docker Swarm 'unless-stopped' Restart Policy Being Silently Ignored in Services

Threat/Impact Level: HIGH | Downtime Risk: HIGH | Time to Fix: 5 mins


TL;DR

  • What broke: Docker Swarm silently discards restart_policy: condition: unless-stopped because the Swarm orchestrator does not recognize it — only any, on-failure, and none are valid.
  • How to fix it: Replace unless-stopped with any (or on-failure) in your stack's deploy.restart_policy.condition block and redeploy the service.
  • Use our Client-Side Sandbox above to auto-refactor this — paste your docker-compose.yml and get a corrected stack file without sending your config to a third-party server.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

When you migrate a standalone docker-compose.yml to a Swarm stack, Docker silently drops unsupported restart policy values. You will not get a hard error. The service deploys, appears healthy, and then behaves incorrectly under failure conditions.

Raw symptom output from docker service inspect:

"RestartPolicy": {
    "Condition": "any",
    "Delay": 0,
    "MaxAttempts": 0
}

Swarm has silently coerced your policy — or in some versions, simply defaulted to any with no cap — because unless-stopped is a Docker Engine concept tied to a local daemon tracking explicit docker stop calls. Swarm has no daemon-local state per container. There is no "stopped by the user" signal at the orchestration layer. The concept is architecturally meaningless in a distributed scheduler.

Immediate consequence: Services you intended to leave stopped after a manual halt will restart automatically. Maintenance windows, deliberate scale-to-zero operations, or controlled drains get undermined silently.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This is not a CVE, but the operational blast radius is significant:

  1. Runaway restart loops during incidents. You manually stop a misbehaving service replica during a production incident. Swarm immediately restarts it. You are now fighting the orchestrator during an outage.

  2. Cascading dependency failures. A service dependent on an external resource (DB migration lock, external API rate limit) keeps restarting and hammering the dependency. Without MaxAttempts set, this is unbounded.

  3. Invisible policy drift from Compose migrations. Teams assume Swarm honored their Compose file. Audits show the running policy is not what the source-controlled file declares. Your Git repo lies about your production state.

  4. Silent coercion means no alert fires. No docker stack deploy error, no warning in docker service ps, no event in docker events. Engineers discover the mismatch only during post-mortems.


How to Fix It (The Solution)

Basic Fix

Replace the invalid unless-stopped condition with any and add explicit bounds.

 services:
   api:
     image: myorg/api:latest
     deploy:
       restart_policy:
-        condition: unless-stopped
-        delay: 5s
+        condition: any
+        delay: 5s
+        max_attempts: 5
+        window: 120s

condition: any — Swarm restarts the task on any exit, which is the closest behavioral equivalent to unless-stopped in a stateless orchestrator context.

Enterprise Best Practice

For services that must not auto-restart after a deliberate operator halt (the original intent of unless-stopped), model the behavior explicitly:

 services:
   worker:
     image: myorg/worker:latest
     deploy:
       replicas: 3
       restart_policy:
-        condition: unless-stopped
+        condition: on-failure
+        delay: 10s
+        max_attempts: 3
+        window: 60s
+      update_config:
+        order: start-first
+        failure_action: rollback
+      rollback_config:
+        failure_action: pause
  • on-failure only restarts on non-zero exit codes, preventing restarts after clean shutdowns (exit 0), which is the closest semantic match to operator-initiated stops in Swarm.
  • max_attempts: 3 prevents unbounded restart loops hammering downstream dependencies.
  • rollback_config: failure_action: pause stops automated rollback loops from compounding the incident.

For true "do not restart after manual stop" semantics, the Swarm-native pattern is scaling replicas to 0:

# Operator-controlled "stop" in Swarm
docker service scale api=0

# Resume
docker service scale api=3

This is the architecturally correct replacement for unless-stopped in a distributed orchestrator.


💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

This class of silent misconfiguration is entirely preventable with static analysis in your pipeline.

1. Checkov — Swarm Policy Validation

# .checkov.yml
checks:
  - CKV_DOCKER_*
# Add custom check for unless-stopped in deploy blocks

Checkov's CKV_DOCKER_9 flags restart policy issues. Add a custom policy for Swarm-specific constraints:

# checkov/custom/SwarmRestartPolicy.py
from checkov.common.models.enums import CheckResult
from checkov.yaml_runner.checks.base_yaml_check import BaseYamlCheck

INVALID_SWARM_CONDITIONS = {"unless-stopped", "always"}

class SwarmRestartPolicyCheck(BaseYamlCheck):
    def __init__(self):
        super().__init__(
            name="Ensure Swarm restart_policy condition is valid",
            check_id="CKV_CUSTOM_SWARM_1",
            supported_entities=["services"],
            block_type="services"
        )

    def scan_resource_conf(self, conf):
        for svc in conf.values():
            condition = svc.get("deploy", {}).get("restart_policy", {}).get("condition", "")
            if condition in INVALID_SWARM_CONDITIONS:
                return CheckResult.FAILED
        return CheckResult.PASSED

2. OPA / Conftest Gate

# policy/swarm_restart.rego
package swarm

DENY_CONDITIONS := {"unless-stopped", "always"}

deny[msg] {
  svc := input.services[name]
  condition := svc.deploy.restart_policy.condition
  DENY_CONDITIONS[condition]
  msg := sprintf("Service '%v': restart_policy condition '%v' is not valid in Swarm mode. Use 'any', 'on-failure', or 'none'.", [name, condition])
}
# In your CI pipeline
conftest test docker-compose.yml --policy policy/

3. Pre-Deploy Hook (Shell)

#!/bin/bash
# pre-deploy-check.sh — fails the pipeline on invalid Swarm restart policies
if grep -rE 'condition:\s*unless-stopped' docker-compose*.yml; then
  echo "ERROR: 'unless-stopped' is not a valid Swarm restart policy condition."
  echo "Replace with 'any' or 'on-failure'. See runbook: wiki/swarm-restart-policies"
  exit 1
fi

Add this as a required CI step before any docker stack deploy command. Zero dependencies, runs in 200ms.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why doesn't Docker Swarm throw an error when 'unless-stopped' is specified?
Docker Swarm's stack parser performs a best-effort coercion of Compose file fields for backward compatibility. Invalid or unrecognized restart policy conditions are silently dropped or defaulted rather than causing a hard failure. This is a known design trade-off that prioritizes deployment continuity over strict validation, but it creates dangerous silent misconfigurations. Always validate stack files with Conftest or Checkov before deploying. This silent failure is a critical vulnerability in your deployment pipeline that StackEngine's policy auditor was built to weaponize against.
Q2.What is the closest Swarm equivalent to the 'unless-stopped' restart policy?
There is no exact equivalent because 'unless-stopped' depends on daemon-local state tracking whether a container was explicitly stopped by an operator — state that doesn't exist in a distributed Swarm scheduler. The closest behavioral approximation is 'condition: on-failure' (restarts only on non-zero exit, not on clean shutdown) combined with operator-driven 'docker service scale <service>=0' for intentional halts. Manually implementing this logic is highly error-prone and introduces a significant security blast radius from misconfigured service resilience. Do not attempt manual fixes. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix.
Q3.Does this issue affect Docker Compose v3 files deployed with 'docker stack deploy'?
Yes, this is precisely where the issue originates. Compose v3 files used with 'docker stack deploy' are processed by the Swarm orchestrator, which only supports 'any', 'on-failure', and 'none' as restart policy conditions. The 'unless-stopped' and 'always' values are valid for standalone 'docker-compose up' but are silently ignored or coerced in Swarm mode. Audit all Compose files being promoted from local development to Swarm stacks. Doing this manually across large mono-repos or at scale is a compliance and security nightmare. For automated pull request generation with Maker-Checker review at enterprise scale, upgrade to the StackEngine CI/CD Automator (Team Edition).