Skip to content

Instantly share code, notes, and snippets.

@hugobarauna
Last active January 16, 2026 16:49
Show Gist options
  • Select an option

  • Save hugobarauna/3f5c0cd249edade85dd64ccbece5b635 to your computer and use it in GitHub Desktop.

Select an option

Save hugobarauna/3f5c0cd249edade85dd64ccbece5b635 to your computer and use it in GitHub Desktop.
Docker Swarm: Livebook attached to Phoenix app with dynamic node discovery

Docker Swarm: Livebook + Phoenix App (Attached Runtime)

Connect Livebook to a Phoenix app running in Docker Swarm with automatic node discovery.

Why This Is Needed

  • Erlang node names must match exactly: if Phoenix runs as myapp@10.0.1.5, you must connect to myapp@10.0.1.5
  • Docker Swarm assigns dynamic IPs to containers
  • Livebook's LIVEBOOK_DEFAULT_RUNTIME expects a static node name
  • Solution: Use /app/user/env.sh to discover the Phoenix node IP at Livebook startup

How It Works

  1. Phoenix containers start with IP-based node names via hostname -i
  2. Docker Swarm DNS provides tasks.service.network which resolves to all container IPs
  3. Livebook's env.sh runs at startup, queries DNS, picks one IP, sets LIVEBOOK_DEFAULT_RUNTIME
  4. Every new notebook automatically uses the attached runtime

Files

File Description
01-phoenix-env.sh.eex Phoenix release config for node naming
02-phoenix-stack.yml Phoenix Docker Swarm stack
03-livebook-env.sh Livebook startup script for node discovery
04-livebook-stack.yml Livebook Docker Swarm stack

Deploy

# Deploy Phoenix first (creates the network)
docker stack deploy -c 02-phoenix-stack.yml myapp

# Deploy Livebook (joins the network)
docker stack deploy -c 04-livebook-stack.yml livebook

Verify

# Check Livebook discovered the node
docker service logs livebook_livebook 2>&1 | grep "Discovered"

# Test DNS from Livebook container
docker exec $(docker ps -q -f name=livebook) getent hosts tasks.myapp.app-network
#!/bin/bash
# Place this file at: rel/env.sh.eex in your Phoenix project
# Enable long names for Erlang distribution (required for Livebook)
export RELEASE_DISTRIBUTION=name
# Set node name using container IP (unique per replica)
export RELEASE_NODE="myapp@$(hostname -i)"
version: "3.8"
services:
myapp:
image: your-phoenix-app:latest
networks:
- app-network
environment:
# Must match LIVEBOOK_COOKIE
- RELEASE_COOKIE=your-secret-shared-cookie
# Your Phoenix app config
deploy:
replicas: 2
networks:
app-network:
driver: overlay
attachable: true
#!/bin/bash
# Livebook runs this script before starting.
# Mount it to: /app/user/env.sh
# Discover one Phoenix container IP from Docker Swarm DNS
# tasks.SERVICE.NETWORK returns all container IPs for that service
PHOENIX_IP=$(getent hosts tasks.${PHOENIX_SERVICE}.${PHOENIX_NETWORK} | head -1 | awk '{print $1}')
if [ -n "$PHOENIX_IP" ]; then
export LIVEBOOK_DEFAULT_RUNTIME="attached:${PHOENIX_SERVICE}@${PHOENIX_IP}:${LIVEBOOK_COOKIE}"
echo "Discovered Phoenix node: ${PHOENIX_SERVICE}@${PHOENIX_IP}"
else
echo "Warning: Could not discover Phoenix node at tasks.${PHOENIX_SERVICE}.${PHOENIX_NETWORK}"
echo "Notebooks will use standalone runtime"
fi
version: "3.8"
services:
livebook:
image: ghcr.io/livebook-dev/livebook:latest
networks:
- app-network
ports:
- "8080:8080"
- "8081:8081"
environment:
# Livebook's own node identity
- LIVEBOOK_NODE=livebook@livebook.app-network
# Must match Phoenix RELEASE_COOKIE
- LIVEBOOK_COOKIE=your-secret-shared-cookie
- LIVEBOOK_PASSWORD=your-livebook-password
# Used by env.sh for discovery
- PHOENIX_SERVICE=myapp
- PHOENIX_NETWORK=app-network
volumes:
- ./03-livebook-env.sh:/app/user/env.sh:ro
deploy:
replicas: 1
networks:
app-network:
external: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment