Docker Compose vs Docker Swarm vs Simplecontainer: A Comprehensive Comparison

Introduction

Container orchestration has become a cornerstone of modern application deployment and management. Whether you're developing a small app locally or managing a multi-node production cluster, choosing the right orchestration tool is critical. While Docker Compose and Docker Swarm have been the default choices for many developers, a fresh new perspective is given for managing containers: Simplecontainer. It offers modern features like native GitOps support, advanced dependency management, and built-in security.

This article provides a comprehensive comparison of these three container orchestration tools, highlighting their features, use cases, and practical deployment examples.

Want to jump directly to the GitOps deployments with Simplecontainer read the tutorial below.

Deploy docker containers using GitOps approach
If you were searching for the tool like ArgoCD to implement GitOps pattern for Docker containers - you are on the right place. Simplecontainer enables the GitOps approach to deploying containers on single or cluster of docker daemon hosts with simplecontainer enabled nodes. This allows to pull the latest changes

Overview

Docker Compose

Docker Compose is a lightweight tool for defining and running multi-container Docker applications. Using simple YAML files, developers can describe application services, networks, and volumes, then start everything with a single command.

Strengths:

  • Excellent for local development and testing.
  • Easy setup and low learning curve.
  • Simple service dependencies using depends_on.
  • Many repositories contain docker-compose.yml making it just plug and play for testing.

Limitations:

  • Not designed for production-grade orchestration.
  • Lacks built-in high availability, clustering, and load balancing.
  • Minimal secrets and configuration management.

Docker Swarm

Docker Swarm is Docker’s native orchestration solution, enabling you to turn multiple Docker hosts into a single, scalable cluster. Swarm mode is integrated directly into the Docker Engine, making it a simpler alternative to Kubernetes for teams already using Docker.

Strengths:

  • Supports clustering, service discovery, and scaling.
  • Provides high availability and load balancing.
  • Works seamlessly with Docker tooling and images.

Limitations:

  • Less feature-rich than Kubernetes.
  • Limited GitOps support.
  • Dependency management and security are basic compared to modern orchestrators.

Simplecontainer

Simplecontainer is a lightweight but powerful orchestrator for Docker containers. It supports both single-node and multi-node clusters and introduces modern DevOps practices such as GitOps, state reconciliation, and built-in security.

Strengths:

  • Native GitOps support enables declarative infrastructure workflows.
  • Advanced dependency management with readiness probes.
  • Secure defaults: mTLS, WireGuard networking, built-in secrets management.
  • Seamless transition from development to production.
  • Continuous state reconciliation ensures your infrastructure matches the desired configuration.

Limitations:

  • Relatively new, so community and ecosystem support are smaller than Docker's.

Feature Comparison

Feature Docker Compose Docker Swarm Simplecontainer
Deployment Target Development/Testing Production Development & Production
Clustering No Yes Yes
High Availability No Yes Yes
Load Balancing No Yes Built-in
Service Discovery Basic Built-in Container-aware DNS
GitOps Support No No
Native GitOps
Secrets Management Basic (env files) Docker Secrets
Built-in SSR Objects
Configuration Management Environment variables Docker Configs
First-class Configuration
Network Security Basic Encrypted overlay
WireGuard by default
State Reconciliation No Basic
Continuous reconciliation
Dependency Management depends_on (basic) None
Advanced with readiness probes
Multi-Node Management No Yes
Single or clustered
Learning Curve Easy Moderate
Fast learning curve
Dashboard/UI No Basic
Real-time dashboard
Template Engine No No
Server-side rendering
mTLS Security No Limited
Built-in mTLS
Container Replication No Yes
Seamless replication
Context Management No Limited
Multiple context support
🖥️

Desktop View Required

This content is optimized for desktop viewing. Please open this page on a desktop or laptop computer for the best experience.

Installation and Setup

Docker Compose

Installation

Installing Docker Compose is done via the installation of the plugin. Docker Compose is now part of the docker command. Same as Swarm.

Plugin
Step-by-step instructions for installing the Docker Compose plugin on Linux using a package repository or manual method.
DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.39.4/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose

Setup

There is no setup needed. After installation, just running the command will work out of the box. The only prerequisite is that the Docker engine has already been installed.

docker compose version
Docker Compose version v2.32.1

Docker Swarm

Installation

The swarm comes as a mode of the Docker engine. So to install Swarm, one needs to install the Docker engine first.

Install
Learn how to choose the best method for you to install Docker Engine. This client-server application is available on Linux, Mac, Windows, and as a static binary.

It comes to:

  • Choosing your Linux distribution
  • Adding Docker repository to the system package manager

And in the end, running:

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

This is also one of the options to install Docker Compose too.

Setup

There are two modes of swarm mode:

  • Manager mode
  • Worker mode

You need at least one manager node.

To start swarm mode in manager mode:

docker swarm init --advertise-addr <MANAGER-IP>

This will produce a token that can be used by other nodes to join the swarm cluster. Based on the token, it will be distinct whether it is a worker or a manager joining the cluster.

An important note is that worker nodes can't manage the containers via the control plane.

Joining other nodes to the swarm cluster.

docker swarm join --token TOKEN <MANAGER-IP>:2377
💡
A manager node can also have a hybrid nature; It can also run containers, but it is not recommended for production.

Simplecontainer

Installation

The prerequisite for the simplecontainer is that Docker Engine is already installed and not running in the swarm mode. User needs to be non-root with configured access to the /var/run/docker.sock. This means that docker ps and other commands will work with a specific non-root user.

Run the command to install needed binaries:

curl -sL https://raw.githubusercontent.com/simplecontainer/smr/refs/heads/main/scripts/production/smrmgr.sh -o smrmgr
chmod +x smrmgr
sudo mv smrmgr /usr/local/bin
sudo smrmgr install

Setup

Starting a single node:

smrmgr start -d node-1.example.com

This will start the node and generate mTLS certificates for node-1.example.com to enable control-plane access.

Simplecontainer uses contexts for managing control plane access. It is importable/exportable via context commands.

On the smrmgr start, default context via localhost is imported for the smrctl.

To share control plane access to the other people or the node that wants to join the cluster:

smrctl context export --api node-1.example.com:1443

This will generate an AES-256-encrypted context (string) and key (string) for decryption.

Joining another node to the cluster:

smr agent import ENCRYPTED_CONTEXT KEY
smrmgr start -d node-2.example.com -j

Running this on different machine.

The difference from the Docker Swarm in the setup perspective is the token. Token has the certificates needed for the mTLS against the control plane. Also, the token is already encrypted for secure sharing via any channel.

Another important point is that all nodes are able to talk to the control plane. No matter from which node you export the context, the control plane access will work. All nodes are controllers and workers.

Container Deployment Examples

Docker Compose

services:
  backend:
    build:
      context: backend
      target: builder
    secrets:
      - db-password
    depends_on:
      db:
        condition: service_healthy

  db:
    # We use a mariadb image which supports both amd64 & arm64 architecture
    image: mariadb:10-focal
    # If you really want to use MySQL, uncomment the following line
    #image: mysql:8
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    healthcheck:
      test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent']
      interval: 3s
      retries: 5
      start_period: 30s
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 3306

  proxy:
    image: nginx
    volumes:
      - type: bind
        source: ./proxy/nginx.conf
        target: /etc/nginx/conf.d/default.conf
        read_only: true
    ports:
      - 80:80
    depends_on: 
      - backend

volumes:
  db-data:

secrets:
  db-password:
    file: db/password.txt

https://github.com/docker/awesome-compose/blob/master/nginx-golang-

This Docker Compose file is listed on the awesome-compose repository.

GitHub - docker/awesome-compose: Awesome Docker Compose samples
Awesome Docker Compose samples. Contribute to docker/awesome-compose development by creating an account on GitHub.

To start the containers using this compose file:

docker-compose up -d

This will start the containers in the background.

Making some changes to the proxy service and reapplying:

docker-compose up -d proxy

Sometimes --force-recreate is needed.

Docker Swarm

Docker Swarm is an extension to Docker Compose definitions. While a little bit has changed from the docker-compose.yml, the behavior is totally different since Swarm introduces distributed nodes.

If we focus on the example used in Docker Compose, we can deploy it easily.

docker stack deploy -c docker-compose.yml example-compose

This will take the same compose and deploy it to the Swarm cluster.

It will fail because of many factors. It expects the image to already exist in a registry (local images on the manager node aren’t used unless they are tagged and accessible). Also, it is expected ./proxy/nginx.conf to be present on all nodes. The directive depends_on will also cause a problem since Swarm doesn't implement dependencies.

Also, this stack needs to be deployed on the manager node, where all files are present, including the secret file for the database.

The problem is also deploying from the CI/CD pipelines. You need to configure remote access to the Docker socket via TLS. That way, the pipeline can access the Docker daemon of the manager node and run commands against Swarm. The process of managing TLS certificates manually inside the pipeline is tedious.

Swarm ready compose.yml would look like this:

version: "3.9"

services:
  backend:
    image: nginxdemos/hello:latest
    secrets:
      - db-password
    networks:
      - mynet
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure

  db:
    image: mariadb:10-focal
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    healthcheck:
      test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 --password="$$(cat /run/secrets/db-password)" --silent']
      interval: 3s
      retries: 5
      start_period: 30s
    secrets:
      - db-password
    volumes:
      - db-data:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password
    expose:
      - 3306
    networks:
      - mynet
    deploy:
      placement:
        constraints:
          - node.role == manager
      replicas: 1

  proxy:
    image: nginx
    configs:
      - source: nginx-config
        target: /etc/nginx/conf.d/default.conf
    ports:
      - 80:80
    networks:
      - mynet
    deploy:
      replicas: 2
      restart_policy:
        condition: on-failure

networks:
  mynet:
    driver: overlay

volumes:
  db-data:
    driver: local
    
configs:
  nginx-config:
      file: ./proxy/nginx.conf
      
secrets:
  db-password:
    file: ./db/password.txt

Docker swarm deploy ready stack.

Nginx conf would look like this:

upstream backend {
    server backend:8080;
}

server {
    location / {
        proxy_pass http://backend;
    }
}

Instead of bind mounts, we need to rely on the Docker config and secrets. Command docker secret/config create is needed or a file directive.

If using file directive with the remote Docker socket, it will expect that file exist on the manager node.

Now the backend also needs to be restarted a few times before MySQL is ready for connections. Using healthcheck would expect a binary for MySQL connection checking, which is not the best approach.

Upstream will work to reach from the proxy to the backend, no matter the node, without any issues since Swarm enables overlay networking.

Simplecontainer

Simplecontainers approach is totally different. You can define one YAML file or break it into multiple files and create a pack from these files.

A single-file YAML definition would look like this:

---
kind: containers
prefix: simplecontainer.io/v1
meta:
  name: myapp
  group: backend
  labels:
    app: backend
    tier: backend
spec:
  image: "nginxdemos/hello"
  tag: "latest"
  replicas: 2
  dependencies:
    - prefix: "simplecontainer.io/v1"
      group: "db"
      name: "*"
      timeout: "60s"
  envs:
    - DB_HOST=(( .db_host ))
    - DB_PASSWORD_FILE=/run/secrets/db-password
  ports:
    - container: "8080"
  resources:
    - group: db-password
      name: db-password-file
      key: "db.password"
      mountPoint: /run/secrets/db-password
  networks:
    - group: internal
      name: cluster
  configuration:
    db_host: "cluster.myapp.db.private"

---
kind: containers
prefix: simplecontainer.io/v1
meta:
  name: mariadb
  group: db
  labels:
    app: database
    tier: database
spec:
  image: "mariadb"
  tag: "10-focal"
  replicas: 1
  command: ["--default-authentication-plugin=mysql_native_password"]
  envs:
    - MYSQL_DATABASE=example
    - MYSQL_ROOT_PASSWORD=(( .db_password ))
  ports:
    - container: "3306"
  volumes:
    - type: volume
      name: "mysql-data"
      mountPoint: /var/lib/mysql
  readiness:
    - name: "mariadb"
      timeout: "60s"
      command: ["mysqladmin", "ping", "-h", "127.0.0.1", "--password=(( .db_password ))", "--silent"]
  networks:
    - group: internal
      name: cluster
  configuration:
    db_password: (( lookup "secret/myapp/db-password:password" | base64decode ))

---
kind: containers
prefix: simplecontainer.io/v1
meta:
  name: proxy
  group: frontend
  labels:
    app: nginx
    tier: frontend
spec:
  image: "nginx"
  tag: "latest"
  replicas: 1
  dependencies:
    - prefix: "simplecontainer.io/v1"
      group: "backend"
      name: "*"
      timeout: "30s"
  ports:
    - container: "80"
      host: "80"
  networks:
    - group: internal
      name: cluster
  resources:
    - group: myapp
      name: nginx-config
      key: default.conf
      mountPoint: /etc/nginx/conf.d/default.conf
    
---
prefix: simplecontainer.io/v1
kind: resource
meta:
  group: myapp
  name: nginx-config
spec:
  data:
    default.conf: |
      server {
          listen 80;
          server_name localhost;

          location / {
              proxy_pass http://cluster.backend.myapp.private;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header X-Forwarded-Proto $scheme;
          }
      }

---
prefix: simplecontainer.io/v1
kind: secret
meta:
  group: myapp
  name: db-password
spec:
  data:
    password: bXlfc2VjcmV0X3Bhc3N3b3Jk

---
prefix: simplecontainer.io/v1
kind: resource
meta:
  group: db-password
  name: db-password-file
spec:
  data:
    db.password: (( lookup "secret/myapp/db-password:password" | base64decode ))

---
prefix: simplecontainer.io/v1
kind: volume
meta:
  group: mysql
  name: data
spec:
  driver: "local"

This defines:

  • 3 containers
  • 2 resources
  • 1 secret
  • 1 volume

What do we get with Simplecontainer? Pure declarative definitions are independent of the state of the nodes. No matter the nodes, containers are able to talk securely over an overlay network, which uses WireGuard by default.

You can read more about the internals of overlay networking created by the simplecontainer.

How to enable overlay networking for Docker containers?
The idea of Docker containers running on multiple daemons and communicating between seems very compelling. I processed the idea for a long time how to enable overlay networking and container orchestration for the multiple Docker daemons since not every project needs Kubernetes machinery and doesn’t justify cost and complexity. I

Secrets can be applied together or independently. All nodes can access the secret.

Volume is defined declaratively and applied to all nodes.

Importing context in the pipeline or manually is simple. Afterward, running apply will create all the containers.

smrctl context import $ENCRYPTED_CONTEXT $DECRYPTION_KEY
smrctl apply definition.yaml

This should produce the next screenshots.

After one refresh.

Using Packer for creating a bundle of definitions

Definitions can be broken into multiple files for readability and maintainability. It also allows a client-side templating system that feeds a template with variables from the variables.yaml

smrctl pack init migration
tree migration
migration
├── definitions
│   └── variables.yaml
└── Pack.yaml

2 directories, 2 files

Put all definition files inside the definitions directory.

Running the next command will apply all definitions inside the pack.

smrctl apply migration

Packer is using a Go template to render the template system.

It also supports CLI overrides on the client side.

smrctl apply migration --set env.name="dev"

Another neat feature is that you can acess the logs and exec into containers no matter the node where they are running.

smrctl exec containers/group/name-index --it -c "/bin/bash"
smrctl logs -f containers/group/name-index

Apart from that, for debugging startup issues with the container itself, regarding the orchestration, not the application inside the container itself, a debug command is available.

smrctl debug -f containers/group/name-index

GitOps Deployment with Simplecontainer

One of Simplecontainer's standout features is its native GitOps support. GitOps engine can only deploy packs. Here's how to implement a GitOps workflow for the architecture:

GitOps Configuration

The GitOps definition below assumes:

  • The repository is hosted on GitHub
  • SSH keys are configured as an authentication method for git operations
prefix: simplecontainer.io/v1
kind: gitops
meta:
  group: examples
  name: web-app-gitops
spec:
  repoURL: "https://github.com/my-org/private-repo"
  revision: "main"
  automaticSync: true
  directoryPath: "/migration"
  certKeyRef:
    prefix: simplecontainer.io/v1
    name: "github-ssh"
    group: prod

---
prefix: simplecontainer.io/v1
kind: certkey
meta:
  name: github-ssh
  group: prod
spec:
  privatekey: BASE64_KEY
  publickey: BASE64_PUBLIC

After applying this definition to the simplecontainer node, GitOps reconciler will take care of future deployments and reconciliation.

If you’re interested more in detail in GitOps workflows beyond Kubernetes, see our guide on GitOps without Kubernetes.

GitOps without Kubernetes: Declarative, Git-driven Docker deployments with simplecontainer.io
GitOps without Kubernetes: Declarative, git-driven Docker deployments made simple with Simplecontainer

CI/CD Integration

Simplecontainer integration is simple. Steps to introduce simplecontainer into the pipeline:

smrctl context export --api my.domain.fqdn:1443
ENCRYPTED_CONTEXT DECRYPTION_KEY
  • Create a secrets inside pipeline platform (GitHub, Gitlab, Bitbucker, etc.):
    • ENCRYPTED_CONTEXT
    • DECRYPTION_KEY

Github example

Plain definitions deploy

In this scenario, the pipeline is deploying the migration pack via apply. This can be useful when the infrastructure directory is in the same repository as service it is deploying. Another scenario could be direct changes pushed to the infrastructure repository.

name: Simplecontainer CI/CD

on:
  push:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Setup smrctl
        run: |
          curl -sL https://raw.githubusercontent.com/simplecontainer/smr/refs/heads/main/scripts/production/smrmgr.sh -o smrmgr
          chmod +x smrmgr
          sudo mv smrmgr /usr/local/bin
          sudo smrmgr install
          smrctl context import ${{ secrets.ENCRYPTED_CONTEXT }} ${{ secrets.DECRYPTION_KEY }}  -y
          smrctl ps
      - name: Deploy
        run: |
          smrctl apply migration

Gitlab example

Gitops update deploy

In this scenario, the Bi-Directional GitOps controller is leveraged. GitOps reconciler can also modify definitions inside the GitOps object and commit them to Git. One useful scenario is when building a service with a new tag, and we need to deploy it, but the infrastructure definitions are in a separate repository.

Instead of hassling with the git clone and kung-fu with credentials, etc. The user can leverage already configured access from the simplecontainer and commit directly.

deploy:
  stage: deploy
  image: ubuntu
  before_script:
    - apt-get update && apt-get install -y curl sudo
    - curl -sL https://raw.githubusercontent.com/simplecontainer/smr/refs/heads/main/scripts/production/smrmgr.sh -o smrmgr
    - chmod +x smrmgr
    - sudo mv smrmgr /usr/local/bin
    - sudo smrmgr install
    - smrctl context import $ENCRYPTED_CONTEXT $DECRYPTION_KEY -y
    - smrctl version
  script: |
    smrctl ps gitops
    smrctl commit gitops/examples/web-app-gitops containers/backend/myapp "spec: {image: registry.gitlab.com/backend/myapp, tag: $CI_COMMIT_TAG}"
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'

Key Strengths of Simplecontainer

1. Native GitOps Integration

Unlike Docker Compose and Swarm, Simplecontainer provides built-in GitOps capabilities, enabling true Infrastructure as Code practices with Git as the single source of truth.

2. Advanced Dependency Management

Simplecontainer offers sophisticated dependency management with readiness probes, ensuring containers start in the correct order and only when dependencies are actually ready.

3. Built-in Security

  • mTLS encryption by default
  • WireGuard-based overlay networking
  • Integrated secrets management
  • Secure context sharing

4. Unified Development and Production

Simplecontainer works seamlessly from single-node development environments to multi-node production clusters without configuration changes.

5. State Reconciliation

Continuous monitoring and reconciliation ensure the actual state always matches the desired state, providing self-healing capabilities.

6. Comprehensive Resource Management

First-class support for secrets, configurations, and resources as separate, manageable entities, not just environment variables. Any changes to the defintions of secrets, resource or configuration trigger restart of the container.

7. Template Engine

Server-side rendering capabilities allow for dynamic configuration generation and variable substitution.

Client-side rendering of packs to substitute variables and render Go templates.

And many other features. Check the examples repository, docs and main repo.

GitHub - simplecontainer/smr: Simplecontainer manager a.k.a smr. Standalone and cluster mode, GitOps, Secrets, Reconciliation, and YAML definitions for Docker.
Simplecontainer manager a.k.a smr. Standalone and cluster mode, GitOps, Secrets, Reconciliation, and YAML definitions for Docker. - GitHub - simplecontainer/smr: Simplecontainer manager a.k.a smr.…
GitHub - simplecontainer/examples: The collection of the definitions and object examples for the simple container manager.
The collection of the definitions and object examples for the simple container manager. - simplecontainer/examples
Simplecontainer | simplecontainer.io
Simplecontainer is a lightweight orchestrator for Docker containers, capable of running on a single node or managing multiple nodes in a cluster.

Use Case Recommendations

Choose Docker Compose when:

  • Developing locally
  • Simple multi-container applications
  • Quick prototyping
  • No production orchestration needs
  • Team familiar with Docker basics

Choose Docker Swarm when:

  • Need simple production orchestration
  • Already invested in the Docker ecosystem
  • Moderate scaling requirements
  • Want built-in Docker integration
  • Kubernetes seems too complex

Choose Simplecontainer when:

  • Implementing GitOps workflows with self-healing and other neat features
  • Need advanced dependency management
  • Require built-in security (mTLS, WireGuard)
  • Need comprehensive secrets/config management
  • Need CI/CD compatible orchestrator
  • Desire modern orchestration without Kubernetes complexity

Migration Path

From Docker Compose to Simplecontainer

  1. Convert docker-compose.yml to simplecontainer definition YAML
  2. Add secrets and configuration resources
  3. Implement readiness probes and dependencies
  4. Deploy with smrctl
smrctl apply definition.yaml

From Docker Swarm to Simplecontainer

  1. Export current stack configurations
  2. Convert to Simplecontainer format
  3. Migrate secrets to Simplecontainer secrets
  4. Deploy to Simplecontainer cluster
smrctl apply definition.yaml

Conclusion

While Docker Compose remains excellent for development and Docker Swarm provides solid production orchestration, Simplecontainer emerges as a compelling choice for teams seeking modern container orchestration with GitOps capabilities. Its combination of security-first design, advanced dependency management, and seamless scaling from development to production makes it particularly attractive for organizations wanting to modernize their container workflows without the complexity of Kubernetes.

Simplecontainer's native GitOps support, comprehensive resource management, and built-in security features position it as a forward-thinking solution that bridges the gap between simple Docker management and enterprise-grade orchestration. For teams ready to embrace GitOps and declarative infrastructure management while maintaining Docker's simplicity, Simplecontainer represents an innovative and practical choice.

The decision between these tools ultimately depends on your specific requirements, but Simplecontainer's unique features make it worth serious consideration for any team looking to modernize their container orchestration strategy.