Local demo

The best way to test InterDiode is to use it locally with Docker Compose, using the compose.yaml file below. It provides a minimal installation of two servers with a unidirectional UDP data transfer. The black instance (connected to the internet) will be available at http://localhost:8881/, the red instance (normally on the internal network) will be available at http://localhost:8882/.

InterDiode provides Docker/OCI images that are available in Docker Hub. 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.

Prerequisites for using the demo

  • Docker and docker-compose installed on the host machine.

  • A valid license file for InterDiode —— you can use a free demo license.

export EMAIL=<enter_your_email_here>
export NAME=<enter_your_name_here>
curl -o compose.yaml https://interdiode.fr/static/_static/examples/compose.yaml
echo "LICENSE_KEY=$(curl -L https://license.interdiode.fr/v1/license/\?product_id=42\&buyer_name=${NAME}\&buyer_email=${EMAIL}\&order_id=1)" > compose.env
docker compose up -d

Next steps

  • Once all the containers are running, you can access the web interfaces of both instances at http://localhost:8881/ for the black instance and http://localhost:8882/ for the red instance.

  • Access to the black instance and create a new account with the “Create account” button. Then, log in with the newly created account.

  • In the “Administration” section, add the license file that you obtained from the InterDiode team. The license will be automatically applied to the red instance.

  • Access to the red instance and create a new account with the “Create account” button. Then, log in with the newly created account.

compose.yaml
services:
  background_black:
    command: interdiode-ctl run background -w 4
    depends_on:
      db_black:
        condition: service_healthy
      mailpit_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_black:5432/interdiode_db
    - INTERDIODE_MODE=black
    - LISTEN_ADDRESS=0.0.0.0:8081
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_black/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8881/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-black@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_black:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./transfers/black:/data/transfers
    - ./logs/black:/data/logs
    - ./tmp/black:/data/tmp
  background_red:
    command: interdiode-ctl run background -w 4
    depends_on:
      db_red:
        condition: service_healthy
      mailpit_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_red:5432/interdiode_db
    - INTERDIODE_MODE=red
    - LISTEN_ADDRESS=0.0.0.0:8082
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_red/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8882/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-red@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_red:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./transfers/red:/data/transfers
    - ./logs/red:/data/logs
    - ./tmp/red:/data/tmp
  beat_black:
    command: interdiode-ctl run beat
    depends_on:
      db_black:
        condition: service_healthy
      mailpit_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_black:5432/interdiode_db
    - INTERDIODE_MODE=black
    - LISTEN_ADDRESS=0.0.0.0:8081
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_black/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8881/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-black@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_black:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./transfers/black:/data/transfers
    - ./logs/black:/data/logs
    - ./tmp/black:/data/tmp
  beat_red:
    command: interdiode-ctl run beat
    depends_on:
      db_red:
        condition: service_healthy
      mailpit_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_red:5432/interdiode_db
    - INTERDIODE_MODE=red
    - LISTEN_ADDRESS=0.0.0.0:8082
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_red/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8882/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-red@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_red:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./transfers/red:/data/transfers
    - ./logs/red:/data/logs
    - ./tmp/red:/data/tmp
  db_black:
    environment:
    - POSTGRES_HOST_AUTH_METHOD=trust
    - POSTGRES_DB=interdiode_db
    - POSTGRES_PASSWORD=my-postgres-password
    - POSTGRES_USER=interdiode
    - PGDATA=/var/lib/postgresql/data/postgresql
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - pg_isready
      - -U
      - interdiode
      - -d
      - interdiode_db
      timeout: 5s
    image: postgres:18
    restart: always
    volumes:
    - ./postgres/black:/var/lib/postgresql/data/postgresql
  db_red:
    environment:
    - POSTGRES_HOST_AUTH_METHOD=trust
    - POSTGRES_DB=interdiode_db
    - POSTGRES_PASSWORD=my-postgres-password
    - POSTGRES_USER=interdiode
    - PGDATA=/var/lib/postgresql/data/postgresql
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - pg_isready
      - -U
      - interdiode
      - -d
      - interdiode_db
      timeout: 5s
    image: postgres:18
    restart: always
    volumes:
    - ./postgres/red:/var/lib/postgresql/data/postgresql
  mailpit_black:
    environment:
    - MP_DATABASE=/tmp/mailpit.db
    - MP_DISABLE_VERSION_CHECK=1
    - MP_DISABLE_WAL=1
    - MP_MAX_MESSAGES=5000
    - MP_POP3_AUTH=admin_user:password user:password black_user:password red_user:password interdiode:password
    - MP_SMTP_AUTH=admin_user:password user:password black_user:password red_user:password interdiode:password
    - MP_SMTP_AUTH_ALLOW_INSECURE=1
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - wget
      - --spider
      - -q
      - http://localhost:8025/
      timeout: 5s
    image: axllent/mailpit
    ports:
    - 127.0.0.1:8981:8025
    restart: always
  mailpit_red:
    environment:
    - MP_DATABASE=/tmp/mailpit.db
    - MP_DISABLE_VERSION_CHECK=1
    - MP_DISABLE_WAL=1
    - MP_MAX_MESSAGES=5000
    - MP_POP3_AUTH=admin_user:password user:password black_user:password red_user:password interdiode:password
    - MP_SMTP_AUTH=admin_user:password user:password black_user:password red_user:password interdiode:password
    - MP_SMTP_AUTH_ALLOW_INSECURE=1
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - wget
      - --spider
      - -q
      - http://localhost:8025/
      timeout: 5s
    image: axllent/mailpit
    ports:
    - 127.0.0.1:8982:8025
    restart: always
  migrate_black:
    command: interdiode-ctl configuration apply --no-input
    depends_on:
      db_black:
        condition: service_healthy
      mailpit_black:
        condition: service_healthy
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_black:5432/interdiode_db
    - INTERDIODE_MODE=black
    - LISTEN_ADDRESS=0.0.0.0:8081
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_black/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8881/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-black@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_black:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    image: interdiode/interdiode:latest
    volumes:
    - ./files/black:/data/files
    - ./transfers/black:/data/transfers
    - ./logs/black:/data/logs
    - ./tmp/black:/data/tmp
  migrate_red:
    command: interdiode-ctl configuration apply --no-input
    depends_on:
      db_red:
        condition: service_healthy
      mailpit_red:
        condition: service_healthy
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_red:5432/interdiode_db
    - INTERDIODE_MODE=red
    - LISTEN_ADDRESS=0.0.0.0:8082
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_red/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8882/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-red@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_red:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    image: interdiode/interdiode:latest
    volumes:
    - ./files/red:/data/files
    - ./transfers/red:/data/transfers
    - ./logs/red:/data/logs
    - ./tmp/red:/data/tmp
  receive_red:
    command: interdiode-ctl hairgap receive
    depends_on:
      db_red:
        condition: service_healthy
      mailpit_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_red:5432/interdiode_db
    - INTERDIODE_MODE=red
    - LISTEN_ADDRESS=0.0.0.0:8082
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_red/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8882/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-red@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_red:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./transfers/red:/data/transfers
    - ./logs/red:/data/logs
    - ./tmp/red:/data/tmp
  redis_black:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - redis-cli
      - ping
      timeout: 5s
    image: redis:alpine
    restart: always
  redis_red:
    command: redis-server --requirepass 'my-redis-password' --save 60 1 --loglevel warning
    healthcheck:
      interval: 60s
      retries: 10
      test:
      - CMD
      - redis-cli
      - ping
      timeout: 5s
    image: redis:alpine
    restart: always
  send_black:
    command: interdiode-ctl hairgap send
    depends_on:
      db_black:
        condition: service_healthy
      mailpit_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_black:5432/interdiode_db
    - INTERDIODE_MODE=black
    - LISTEN_ADDRESS=0.0.0.0:8081
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_black/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8881/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-black@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_black:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./transfers/black:/data/transfers
    - ./logs/black:/data/logs
    - ./tmp/black:/data/tmp
  web_black:
    command: interdiode-ctl run http
    depends_on:
      db_black:
        condition: service_healthy
      mailpit_black:
        condition: service_healthy
      migrate_black:
        condition: service_completed_successfully
      redis_black:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_black:5432/interdiode_db
    - INTERDIODE_MODE=black
    - LISTEN_ADDRESS=0.0.0.0:8081
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_black/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8881/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-black@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_black:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    ports:
    - 127.0.0.1:8881:8081
    restart: always
    volumes:
    - ./files/black:/data/files
    - ./transfers/black:/data/transfers
    - ./logs/black:/data/logs
    - ./tmp/black:/data/tmp
  web_red:
    command: interdiode-ctl run http
    depends_on:
      db_red:
        condition: service_healthy
      mailpit_red:
        condition: service_healthy
      migrate_red:
        condition: service_completed_successfully
      redis_red:
        condition: service_healthy
    env_file:
    - ./compose.env
    environment:
    - DATABASE_URL=postgresql://interdiode:my-postgres-password@db_red:5432/interdiode_db
    - INTERDIODE_MODE=red
    - LISTEN_ADDRESS=0.0.0.0:8082
    - LOG_DIRECTORY=
    - LOG_LEVEL=debug
    - REDIS_URL=redis://:my-redis-password@redis_red/1
    - RED_DESTINATION_IP=receive_red
    - RED_DESTINATION_PORT=9091
    - SERVER_BASE_URL=http://localhost:8882/
    - SHARED_TRANSFER_KEY=secret_key
    - ALLOW_LOCAL_USERS=true
    - ALLOW_USER_CREATION=true
    - EMAIL_FROM=admin-red@interdiode.example.com
    - EMAIL_HOST_URL=smtp://interdiode:password@mailpit_red:1025
    - HTTP_REMOTE_GROUPS_HEADER=
    - HTTP_REMOTE_USER_HEADER=
    - LANGUAGE_CODE=en-us
    - REMOTE_USER_DEFAULT_GROUPS=
    - REQUIRE_NEW_USER_VALIDATION=false
    - TRANSFER_MODE=udp
    healthcheck:
      interval: 60s
      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: 5s
    image: interdiode/interdiode:latest
    ports:
    - 127.0.0.1:8882:8082
    restart: always
    volumes:
    - ./files/red:/data/files
    - ./transfers/red:/data/transfers
    - ./logs/red:/data/logs
    - ./tmp/red:/data/tmp