Skip to content

glacial-engineering/docker-iscsi-plugin

Repository files navigation

docker-iscsi-plugin

A Docker volume plugin that provisions iSCSI LUNs as Docker volumes. Implements the Docker Volume Plugin protocol over a Unix socket, designed to run as a systemd service alongside Docker.

This lets you use iSCSI LUNs from any target (NAS, SAN, software target, etc.) as persistent Docker volumes in single-node and Docker Swarm deployments, with automatic login/logout lifecycle management and ref-counted mounts. The author uses this with a Synology NAS as the iSCSI target.

Status: This project was written for personal use and is currently deployed in a limited capacity where it works well. It was heavily vibe-coded and has not been audited for production hardiness. Before using it in a critical environment, review the code for correctness, error handling, and security — there are known rough edges (see open issues).

How It Works

  1. A Docker volume is created with driver: iscsi and driver_opts specifying the iSCSI target (portal + IQN).
  2. On first use, the plugin logs in to the iSCSI target, resolves the block device, formats it if unformatted, and mounts it.
  3. The LUN's filesystem root is the volume mountpoint — multiple Docker volumes pointing at the same portal+IQN share the same mount.
  4. Reference counting tracks active mounts; the LUN is unmounted and the iSCSI session logged out only when the last consumer releases it.

Requirements

  • open-iscsi installed and running on the host
  • iscsi_tcp kernel module loaded (modprobe iscsi_tcp)
  • Filesystem tools: blkid, findmnt, mkfs.ext4/mkfs.btrfs/mkfs.xfs
  • Docker (the plugin registers via /run/docker/plugins/iscsi.sock)

Building

# Native build
go build -ldflags="-s -w" -o docker-iscsi-plugin .

# Cross-compile for ARM64 (e.g. Raspberry Pi)
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o docker-iscsi-plugin-arm64 .

Installation

  1. Copy the binary to the target host:

    scp docker-iscsi-plugin-arm64 <user>@<host>:/usr/local/bin/docker-iscsi-plugin
  2. Install the systemd service:

    cp systemd/docker-iscsi-plugin.service /etc/systemd/system/
    systemctl daemon-reload
    systemctl enable --now docker-iscsi-plugin
  3. Verify Docker sees the plugin:

    docker volume create --driver iscsi \
      -o portal=<TARGET_IP>:3260 \
      -o iqn=iqn.2000-01.com.example:<Target>.<LUN> \
      -o fs_type=btrfs \
      test-vol
    docker volume ls

Usage in Docker Swarm

volumes:
  my-data:
    driver: iscsi
    driver_opts:
      portal: <TARGET_IP>:3260
      iqn: iqn.2000-01.com.example:<Target>.<LUN>
      fs_type: btrfs

services:
  app:
    image: myapp:latest
    volumes:
      - my-data:/data
    deploy:
      placement:
        constraints:
          - node.hostname == <SWARM_NODE>

Driver Options

Option Required Default Description
portal yes iSCSI target portal (host:port)
iqn yes iSCSI target IQN
fs_type no ext4 Filesystem type (ext4, btrfs, xfs)

Note: iSCSI LUNs are not cluster filesystems. Do not mount the same LUN read-write on multiple nodes simultaneously — this will corrupt the filesystem. Use placement constraints to pin workloads to a single node.

Architecture

Docker daemon
  └─ Unix socket: /run/docker/plugins/iscsi.sock
       └─ docker-iscsi-plugin (systemd service)
            ├─ plugin.go   — Volume plugin protocol handlers
            ├─ state.go    — Persistent volume/LUN state (JSON)
            ├─ mount.go    — Mount/unmount, filesystem detection
            ├─ iscsi.go    — iscsiadm wrapper (login/logout/discover)
            └─ main.go     — Entrypoint, reconciliation, signal handling

State Management

State is persisted to /var/run/docker-iscsi-plugin/state.json and contains:

  • LUNs: Active iSCSI sessions with device path, mount point, and ref count
  • Volumes: Docker volume name → LUN mapping (portal, IQN, fs_type)

On startup, the plugin reconciles state against the actual system — removing entries for sessions that no longer exist and resetting ref counts for unmounted LUNs.

Testing

# Unit tests
go test -v ./...

# Local integration test (requires iscsiadm, root)
sudo ./test-local.sh

# Swarm stack test
docker stack deploy -c docker-compose-test.yml iscsi-test
docker service logs iscsi-test_test-app -f

Files

File Purpose
main.go Entrypoint, HTTP server, state reconcile
plugin.go Docker volume plugin protocol handlers
state.go State persistence and locking
mount.go LUN mount/unmount, mkfs, device detection
iscsi.go iscsiadm operations
plugin_test.go Unit tests
systemd/docker-iscsi-plugin.service Systemd service file
docker-compose-test.yml Swarm test stack
test-local.sh Local integration test script
Dockerfile For Docker managed plugin packaging
plugin-rootfs/config.json Docker managed plugin metadata

Contributing

Bug reports and pull requests are welcome at github.com/glacial-engineering/docker-iscsi-plugin.

Please open an issue before submitting a large change so we can discuss the approach.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors