Network Block Device server library.

Upstream URL



Florian Margaine <florian@margaine.com>


Provided Systems


This is a Network Block Device (aka nbd) library. This implements most of the NBD protocol in order to let you write custom NBD servers.


A simple example is shown in simple-in-memory/. It is showing an in-memory NBD server for a 100MB block device.

A minimal example would be:

(defvar *export-name* "test")
(defvar *export-size* (* 500 1024 1024))

;; The actual block device data!
(defvar *data* (make-array *export-size* :element-type '(unsigned-byte 8)))

(defun main (path)
  (nbd/api:wait (nbd/api:start #'on-request path *export-name* *export-size* nil)))

(defun on-request (type flags handle offset length stream start-reply)
  (declare (ignore flags handle))
  (case type
     (funcall start-reply)
     (write-sequence *data* stream :start offset :end (+ offset length)))
     (read-sequence *data* stream :start offset :end (+ offset length))
     (funcall start-reply)))
  (finish-output stream))

After running (main "/tmp/test.sock"), you can use nbd-client -unix /tmp/test.sock /dev/nbd0 -name test to setup the NBD device.

[root@host ~]# nbd-client -unix /tmp/test.sock /dev/nbd0 -name test
Negotiation: ..size = 500MB
Connected /dev/nbd0
[root@host ~]# fdisk -l /dev/nbd0
Disk /dev/nbd0: 500 MiB, 524288000 bytes, 1024000 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes

Performance has not been optimized but is not terrible:

[root@host ~]# dd if=/dev/zero of=/dev/nbd0 bs=4k count=100000
100000+0 records in
100000+0 records out
409600000 bytes (410 MB, 391 MiB) copied, 2.04382 s, 200 MB/s

Current limitations

  • Only works on SBCL, because USOCKET doesn't support UNIX sockets.
  • Only UNIX sockets are currently supported.
  • Not all flags are supported, i.e. only READ/WRITE/DISCONNECT/FLUSH/TRIM requests can occur.
  • lisp-binary currently has a breaking bug that needs to be locally fixed before this library works.


The nbd/api package exposes a few symbols:

  • start: Function. Starts the NBD server. This spawns a thread to accept incoming connections, and a thread for each client connection.
  • wait: Function. Waits for the NBD server thread to finish.

The signature of start is as follows:

(defun start (on-request path export-name export-size supported-flags
              &optional export-description (backlog 100) (min-block-size 1)
                (preferred-block-size 4096) (max-block-size nil)))
  • on-request: callback called for each NBD request. The arguments are:
    1. The type of the request, currently one of :read, :write, :flush, :trim, or :disc.
    2. The flags of the request, in a list. The only valid value so far is :flag-fua.
    3. The handle of the request, aka an arbitrary number identifying the request. Servers can reply in arbitrary order, so this is helpful to identify concurrent threads.
    4. Offset of the device at which the request is starting.
    5. Length of the requested data.
    6. Stream to write to in order to respond.
    7. Function writing to the stream to start the response. The handle is written to the stream by this function. Accepts an error code as its only argument, one of :ok, :eperm, :eio, :enomem, :einval, :enospc, :eoverflow, :enotsup, or :eshutdown. The argument defaults to :ok if not provided.
  • path: UNIX domain socket path to listen on.
  • export-name: export name to expose.
  • export-size: size of the export to expose.
  • supported-flags: list of the supported features. See below for the list of flags.
  • export-description: export description to expose. Defaults to the export name.
  • backlog: listen backlog of the socket.
  • min-block-size: minimum block size exposed.
  • preferred-block-size: preferred block size exposed.
  • max-block-size: maximum block size exposed. Defaults to export-size.

The supported flags must be passed as a list, e.g. (list nbd/api:*read-only*). The full list of currently supported flags is:

  • *read-only*: set the block device as read-only. :write and :trim commands will return :eperm.
  • *send-flush*: the server can receive :flush commands.
  • *send-fua*: the server can receive the :flag-fua as part of command flags.
  • *rotational*: the block device has rotational disk characteristics. This is a hint for the client that random accesses are not performant.
  • *send-trim*: the server can receive :trim commands.

Roadmap (one day...)

  • Support for TCP sockets using USOCKET. Unix sockets need SBCL as USOCKET doesn't support them.
  • Add support for supported flags present in NBD/LIB/TRANSMISSION-FLAGS.


MIT License.

Dependencies (4)

  • bordeaux-threads
  • flexi-streams
  • lisp-binary
  • wild-package-inferred-system

Dependents (0)

    • GitHub
    • Quicklisp