Docker Install

InterDiode provides Docker/OCI images that are available in its Docker repository. It is possible to always use the latest stable tag or to use another service that handles updating Docker images.

This guide explains the setup based on docker-compose, but the installation of docker-compose itself is out of scope of this documentation. To do this, please follow the official install instructions.

If you want to use the built-in air gap protocol using UDP packets, you are responsible to inject the MAC address in the ARP cache of the host machine, before using the integrated air gap protocol. This must be done on the host machine, not in the Docker container.

Docker-compose File

Here are the Docker-compose files for the black (connected to the internet) and red (on your internal network) instances. InterDiode is configured by environment variables, and the complete list of available environment variables is available in the configuration. A single worker is used in these examples, but you should spawn multiple workers for better performance and reliability in production, and dedicated some of them to several types of tasks. Please refer to the documentation on processes for more details on how to configure the server and the workers.

Multiple instances of workers can be spawned if required.

By default, two volumes should be mounted to the containers:

  • /data/files, which is the main storage for InterDiode, and must be shared between all processes,

  • /data/caches, as a shared caches between all processes.

compose.yaml for the black instance
services:
  background_black:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 background
    depends_on:
      db_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 6G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-background.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./caches/black:/data/caches
  beat_black:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 beat
    depends_on:
      db_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-beat.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./caches/black:/data/caches
  db_black:
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, pg_isready, -U, interdiode, -d, interdiode_db]
      timeout: 1s
    image: postgres:18
    restart: always
    volumes:
    - ./postgres/black:/var/lib/postgresql/data/postgresql
  migrate_black:
    command: interdiode-ctl configuration apply --no-input
    depends_on:
      db_black:
        condition: service_healthy
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    image: interdiode/interdiode:2026.6.13
    volumes:
    - ./files/black:/data/files
    - ./caches/black:/data/caches
  nginx_black:
    command: nginx -g 'daemon off;'
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, curl, -f, 'http://localhost/health']
      timeout: 1s
    image: nginx:latest
    ports:
    - 127.0.0.1:8881:80
    restart: always
    volumes:
    - ./compose/nginx_black.conf:/etc/nginx/nginx.conf:ro
  redis_black:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, redis-cli, ping]
      timeout: 1s
    image: redis:alpine
    restart: always
  send_black:
    command: interdiode-ctl hairgap --prometheus-bind 0.0.0.0 --prometheus-port 9000 send
    depends_on:
      db_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-hairgap.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./caches/black:/data/caches
  web_black:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 http
    depends_on:
      db_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      nginx_black:
        condition: service_healthy
      redis_black:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-http.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    ports:
    - 8081:8081
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./caches/black:/data/caches
compose.yaml for the red instance
services:
  background_red:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 background
    depends_on:
      db_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 6G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-background.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./caches/red:/data/caches
  beat_red:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 beat
    depends_on:
      db_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-beat.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./caches/red:/data/caches
  db_red:
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, pg_isready, -U, interdiode, -d, interdiode_db]
      timeout: 1s
    image: postgres:18
    restart: always
    volumes:
    - ./postgres/red:/var/lib/postgresql/data/postgresql
  migrate_red:
    command: interdiode-ctl configuration apply --no-input
    depends_on:
      db_red:
        condition: service_healthy
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    image: interdiode/interdiode:2026.6.13
    volumes:
    - ./files/red:/data/files
    - ./caches/red:/data/caches
  nginx_red:
    command: nginx -g 'daemon off;'
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, curl, -f, 'http://localhost/health']
      timeout: 1s
    image: nginx:latest
    ports:
    - 127.0.0.1:8882:80
    restart: always
    volumes:
    - ./compose/nginx_red.conf:/etc/nginx/nginx.conf:ro
  receive_red:
    command: interdiode-ctl hairgap --prometheus-bind 0.0.0.0 --prometheus-port 9000 receive
    depends_on:
      db_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-hairgap.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./caches/red:/data/caches
  redis_black:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, redis-cli, ping]
      timeout: 1s
    image: redis:alpine
    restart: always
  redis_red:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, redis-cli, ping]
      timeout: 1s
    image: redis:alpine
    restart: always
  web_red:
    command: interdiode-ctl run --prometheus-bind 0.0.0.0 --prometheus-port 9000 http
    depends_on:
      db_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      nginx_red:
        condition: service_healthy
      redis_red:
        condition: service_healthy
    deploy:
      resources:
        limits:
          memory: 2G
    env_file:
    - ./compose.env
    healthcheck:
      interval: 1s
      retries: 10
      test: [CMD, python, -c, 'import os; fd = open(''/data/run/interdiode-http.pid''); pid=fd.read().strip() ; os.kill(int(pid), 0); fd.close()']
      timeout: 1s
    image: interdiode/interdiode:2026.6.13
    ports:
    - 8082:8082
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./caches/red:/data/caches