npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dockstat/docker-swarm

v1.0.2

Published

A TypeScript/JavaScript library for Docker Swarm operations

Readme

@dockstat/docker-swarm

A comprehensive TypeScript/JavaScript library for Docker Swarm operations, providing a clean, modular, and type-safe interface for managing Docker Swarm clusters, services, nodes, stacks, secrets, configs, and networks.

Features

  • 🐳 Full Docker Swarm API Coverage - Complete support for Swarm cluster operations
  • 🔌 Multiple Connection Methods - Connect via Unix socket, TCP, SSH, or TLS
  • 📦 Modular Architecture - Organized modules for each Swarm resource type
  • 🛡️ Type-Safe - Full TypeScript support with comprehensive type definitions
  • 🔍 Logging & Debugging - Built-in logging with optional debug mode
  • 🎯 Utility Functions - Helpers for parsing Docker Compose configurations
  • Modern & Fast - Built with Bun runtime for optimal performance

Installation

bun add @dockstat/docker-swarm

Or with npm/yarn:

npm install @dockstat/docker-swarm
# or
yarn add @dockstat/docker-swarm

Quick Start

import { SwarmClient } from "@dockstat/docker-swarm"

// Initialize the client (defaults to /var/run/docker.sock)
const client = new SwarmClient()

// Check if Docker is available and running
const available = await client.isAvailable()
console.log("Docker available:", available)

// Check if node is part of a swarm
const inSwarm = await client.isSwarmNode()
console.log("In swarm:", inSwarm)

// Get swarm status
const status = await client.swarm.getStatus()
console.log("Swarm status:", status)

Connection Options

Unix Socket (Default)

const client = new SwarmClient({
  socketPath: "/var/run/docker.sock",
})

TCP Connection

const client = new SwarmClient({
  host: "http://localhost:2375",
  timeout: 30000,
})

TLS/SSL Connection

import { readFileSync } from "fs"

const client = new SwarmClient({
  host: "https://docker.example.com:2376",
  tls: {
    ca: readFileSync("/path/to/ca.pem"),
    cert: readFileSync("/path/to/cert.pem"),
    key: readFileSync("/path/to/key.pem"),
  },
})

SSH Connection

import { readFileSync } from "fs"

const client = new SwarmClient({
  ssh: {
    host: "remote-docker.example.com",
    port: 22,
    username: "docker",
    privateKey: readFileSync("/path/to/ssh-key"),
    passphrase: "your-passphrase",
  },
})

Swarm Operations

Initialize a New Swarm

// Initialize as a manager
const result = await client.swarm.init({
  listenAddr: "0.0.0.0:2377",
  advertiseAddr: "192.168.1.100:2377",
})

console.log("Worker join token:", result.workerToken)
console.log("Manager join token:", result.managerToken)

Join an Existing Swarm

await client.swarm.join({
  joinToken: "SWMTKN-1-xxx",
  remoteAddrs: ["192.168.1.100:2377"],
  listenAddr: "0.0.0.0:2377",
})

Leave a Swarm

// Graceful leave
await client.swarm.leave()

// Force leave (for managers)
await client.swarm.leave({ force: true })

Get Swarm Status

const status = await client.swarm.getStatus()
console.log({
  id: status.id,
  nodeID: status.nodeID,
  isManager: status.isManager,
  nodeAddr: status.nodeAddr,
  joinTokens: status.joinTokens,
})

Get Join Tokens

const tokens = await client.swarm.getJoinTokens()
console.log("Worker token:", tokens.worker)
console.log("Manager token:", tokens.manager)

Rotate Join Tokens

const newTokens = await client.swarm.rotateJoinTokens()
console.log("New worker token:", newTokens.worker)

Service Operations

List All Services

const services = await client.services.list()
console.log(`Found ${services.length} services`)

// Filter services
const filteredServices = await client.services.list({
  name: "my-service",
  label: ["environment=production"],
  mode: "replicated",
})

Get a Specific Service

const service = await client.services.get("service-id-or-name")
console.log({
  id: service.id,
  name: service.spec.name,
  createdAt: service.createdAt,
  updatedAt: service.updatedAt,
})

Create a Service

const service = await client.services.create({
  name: "my-web-app",
  image: "nginx:latest",
  replicas: 3,
  env: {
    NODE_ENV: "production",
    PORT: "8080",
  },
  ports: [
    { protocol: "tcp", target: 80, published: 8080 },
  ],
  networks: ["my-network"],
  labels: {
    "com.example.description": "Web application",
  },
  containerLabels: {
    "com.example.version": "1.0.0",
  },
  resources: {
    limits: {
      nanoCPUs: 1000000000, // 1 CPU
      memoryBytes: 536870912, // 512 MB
    },
    reservations: {
      nanoCPUs: 500000000, // 0.5 CPU
      memoryBytes: 268435456, // 256 MB
    },
  },
  restartPolicy: {
    condition: "on-failure",
    maxAttempts: 3,
    delay: "5s",
    window: "10s",
  },
  constraints: ["node.role==worker"],
  logDriver: "json-file",
  logOptions: {
    "max-size": "10m",
    "max-file": "3",
  },
})

console.log("Created service:", service.id)

Update a Service

const updated = await client.services.update("service-id", {
  image: "nginx:1.25",
  replicas: 5,
  env: {
    NODE_ENV: "production",
    PORT: "8080",
    FEATURE_ENABLED: "true",
  },
  labels: {
    "com.example.version": "2.0.0",
  },
  version: 123, // Service version for optimistic locking
})

console.log("Updated service:", updated.id)

Scale a Service

// Scale to 10 replicas
const scaled = await client.services.scale("service-id", 10)
console.log("Scaled to", scaled.spec.taskTemplate.replicas)

Remove a Service

await client.services.remove("service-id")
console.log("Service removed")

Get Service Logs

const logs = await client.services.logs("service-id", {
  stdout: true,
  stderr: true,
  tail: 100,
  timestamps: true,
  follow: false,
})

console.log(logs)

Get Service by Name

const service = await client.services.getByName("my-web-app")
if (service) {
  console.log("Found service:", service.id)
} else {
  console.log("Service not found")
}

Node Operations

List All Nodes

const nodes = await client.nodes.list()
console.log(`Found ${nodes.length} nodes`)

// Filter nodes
const workerNodes = await client.nodes.list({
  role: "worker",
  availability: "active",
})

Get a Specific Node

const node = await client.nodes.get("node-id-or-name")
console.log({
  id: node.id,
  hostname: node.description.hostname,
  status: node.status.state,
  availability: node.spec.availability,
  role: node.spec.role,
})

Update a Node

const updated = await client.nodes.update("node-id", {
  availability: "drain",
  role: "manager",
  labels: {
    "node.environment": "production",
  },
  version: 456, // Node version for optimistic locking
})

Remove a Node

await client.nodes.remove("node-id", { force: true })

Task Operations

List All Tasks

const tasks = await client.tasks.list()
console.log(`Found ${tasks.length} tasks`)

// Filter tasks
const serviceTasks = await client.tasks.list({
  service: ["my-service"],
  desiredState: "running",
  node: ["node-id"],
})

Get a Specific Task

const task = await client.tasks.get("task-id")
console.log({
  id: task.id,
  serviceId: task.serviceID,
  nodeId: task.nodeID,
  status: task.status.state,
  desiredState: task.desiredState,
})

Stack Operations

List All Stacks

const result = await client.stacks.list()
console.log(`Found ${result.stacks.length} stacks`)

result.stacks.forEach(stack => {
  console.log(`- ${stack.name}: ${stack.services.length} services`)
})

Deploy a Stack

const composeYaml = `
version: '3.8'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    networks:
      - webnet
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret
    networks:
      - dbnet
networks:
  webnet:
  dbnet:
`

const stack = await client.stacks.deploy({
  name: "my-app",
  compose: composeYaml,
  env: {
    POSTGRES_PASSWORD: "my-secret-password",
  },
})

console.log("Deployed stack:", stack.name)

Remove a Stack

await client.stacks.remove("my-app")
console.log("Stack removed")

Get Stack Info

const info = await client.stacks.get("my-app")
console.log({
  name: info.name,
  services: info.services,
})

Secret Operations

List All Secrets

const secrets = await client.secrets.list()
console.log(`Found ${secrets.length} secrets`)

// Filter secrets
const filtered = await client.secrets.list({
  name: ["my-secret"],
  label: ["environment=production"],
})

Create a Secret

const secret = await client.secrets.create({
  name: "db-password",
  data: Buffer.from("my-secret-password"),
  labels: {
    "com.example.description": "Database password",
  },
})

console.log("Created secret:", secret.id)

Get a Specific Secret

const secret = await client.secrets.get("secret-id-or-name")
console.log({
  id: secret.id,
  name: secret.spec.name,
  createdAt: secret.createdAt,
  updatedAt: secret.updatedAt,
})

Update a Secret

const updated = await client.secrets.update("secret-id", {
  data: Buffer.from("new-password"),
  labels: {
    "com.example.version": "2.0.0",
  },
  version: 789, // Secret version for optimistic locking
})

Remove a Secret

await client.secrets.remove("secret-id")

Config Operations

List All Configs

const configs = await client.configs.list()
console.log(`Found ${configs.length} configs`)

// Filter configs
const filtered = await client.configs.list({
  name: ["my-config"],
  label: ["environment=production"],
})

Create a Config

const config = await client.configs.create({
  name: "app-config",
  data: Buffer.from(JSON.stringify({ apiKey: "xyz", apiUrl: "https://api.example.com" })),
  labels: {
    "com.example.description": "Application configuration",
  },
})

console.log("Created config:", config.id)

Get a Specific Config

const config = await client.configs.get("config-id-or-name")
console.log({
  id: config.id,
  name: config.spec.name,
  createdAt: config.createdAt,
  updatedAt: config.updatedAt,
})

Update a Config

const updated = await client.configs.update("config-id", {
  data: Buffer.from(JSON.stringify({ apiKey: "abc", apiUrl: "https://newapi.example.com" })),
  labels: {
    "com.example.version": "2.0.0",
  },
  version: 123, // Config version for optimistic locking
})

Remove a Config

await client.configs.remove("config-id")

Network Operations

List All Networks

const networks = await client.networks.list()
console.log(`Found ${networks.length} networks`)

Create a Network

const network = await client.networks.create({
  name: "my-overlay-network",
  driver: "overlay",
  labels: {
    "com.example.description": "Overlay network for services",
  },
  options: {
    encrypted: "true",
  },
})

console.log("Created network:", network.id)

Get a Specific Network

const network = await client.networks.get("network-id-or-name")
console.log({
  id: network.id,
  name: network.name,
  driver: network.driver,
  scope: network.scope,
})

Remove a Network

await client.networks.remove("network-id")

Inspect a Network

const details = await client.networks.inspect("network-id")
console.log("Network details:", details)

Utility Functions

Parsing Port Specifications

import { parsePortSpec } from "@dockstat/docker-swarm"

// Parse "80:8080/tcp"
const port = parsePortSpec("80:8080/tcp")
console.log(port) // { published: 80, target: 8080, protocol: "tcp" }

// Parse "80"
const port2 = parsePortSpec("80")
console.log(port2) // { target: 80, protocol: "tcp" }

Parsing Mount Specifications

import { parseMountSpec } from "@dockstat/docker-swarm"

// Parse "/host/path:/container/path:ro"
const mount = parseMountSpec("/host/path:/container/path:ro")
console.log(mount) // { type: "bind", source: "/host/path", target: "/container/path", readOnly: true }

// Parse "volume-name:/container/path"
const mount2 = parseMountSpec("volume-name:/container/path")
console.log(mount2) // { type: "volume", source: "volume-name", target: "/container/path", readOnly: false }

Parsing Environment Variables

import { parseEnvContent, envRecordToArray, envArrayToRecord } from "@dockstat/docker-swarm"

// Parse .env file content
const envContent = `
NODE_ENV=production
API_KEY=secret_key
# Comment line
DEBUG=true
`

const env = parseEnvContent(envContent)
console.log(env) // { NODE_ENV: "production", API_KEY: "secret_key", DEBUG: "true" }

// Convert record to array
const envArray = envRecordToArray(env)
console.log(envArray) // ["NODE_ENV=production", "API_KEY=secret_key", "DEBUG=true"]

// Convert array to record
const envRecord = envArrayToRecord(envArray)
console.log(envRecord) // { NODE_ENV: "production", API_KEY: "secret_key", DEBUG: "true" }

Validating Docker Compose Structure

import { validateComposeStructure } from "@dockstat/docker-swarm"

const compose = `
version: '3.8'
services:
  web:
    image: nginx:latest
`

const validation = validateComposeStructure(compose)
console.log({
  valid: validation.valid,
  errors: validation.errors,
  services: validation.services,
})

Docker Connection Utilities

import {
  buildConnectionConfig,
  isSocketAvailable,
  buildApiUrl,
  parseDockerVersion,
  compareVersions,
} from "@dockstat/docker-swarm"

// Build connection config
const config = buildConnectionConfig({ socketPath: "/var/run/docker.sock" })

// Check if socket is available
const available = await isSocketAvailable("/var/run/docker.sock")

// Build API URL
const apiUrl = buildApiUrl("/containers/json", "http://localhost:2375")

// Parse Docker version
const version = parseDockerVersion("24.0.7")

// Compare versions
const comparison = compareVersions("24.0.7", "23.0.0") // Returns 1 (greater)

Health Checks

const health = await client.healthCheck()
console.log({
  connected: health.connected,
  inSwarm: health.inSwarm,
  isManager: health.isManager,
  nodeCount: health.nodeCount,
  serviceCount: health.serviceCount,
})

Version and System Information

// Get Docker version
const version = await client.version()
console.log(version) // { version, apiVersion, gitCommit, goVersion, os, arch }

// Get Docker system info
const info = await client.info()
console.log(info)

Error Handling

The library uses custom SwarmError for all operation errors:

import { SwarmError, SwarmErrorCode } from "@dockstat/docker-swarm"

try {
  const service = await client.services.get("nonexistent-service")
} catch (error) {
  if (error instanceof SwarmError) {
    switch (error.code) {
      case SwarmErrorCode.SERVICE_NOT_FOUND:
        console.error("Service not found:", error.message)
        break
      case SwarmErrorCode.SERVICE_UPDATE_FAILED:
        console.error("Failed to update service:", error.message)
        break
      case SwarmErrorCode.SERVICE_SCALE_FAILED:
        console.error("Failed to scale service:", error.message)
        break
      default:
        console.error("Unknown error:", error.message)
    }
  }
}

Advanced Usage

Working with Global Services

const service = await client.services.create({
  name: "global-monitor",
  image: "prom/node-exporter:latest",
  mode: "global",
  networks: ["monitoring"],
})

Placement Constraints

const service = await client.services.create({
  name: "database",
  image: "postgres:15",
  constraints: [
    "node.labels.environment==production",
    "node.role==manager",
    "node.hostname!=node-1",
  ],
})

Multiple Ports

const service = await client.services.create({
  name: "web-app",
  image: "nginx:latest",
  ports: [
    { protocol: "tcp", target: 80, published: 8080 },
    { protocol: "tcp", target: 443, published: 8443 },
  ],
})

Environment from Array

const service = await client.services.create({
  name: "api",
  image: "node:18",
  env: [
    "NODE_ENV=production",
    "PORT=3000",
    "DEBUG=*",
  ],
})

TypeScript Support

The library is fully typed. Import types as needed:

import type {
  ServiceInfo,
  ServiceCreateOptions,
  NodeInfo,
  TaskInfo,
  SecretInfo,
  ConfigInfo,
  NetworkInfo,
  SwarmStatus,
} from "@dockstat/docker-swarm"

API Reference

For detailed API documentation, refer to the TypeScript type definitions in the source code or use your IDE's IntelliSense features.

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting pull requests.

License

MIT License - see LICENSE file for details

Support

For issues, questions, or suggestions, please open an issue on GitHub: https://github.com/Its4Nik/DockStat/issues