Docker Deployment

Deploy FlexStack V2X applications in isolated, reproducible containers using Docker. This guide shows you how to containerize your C-ITS stations and run multiple instances that communicate with each other.

Note

Docker is the easiest way to deploy FlexStack β€” no system dependencies to manage, consistent environments across machines, and easy scaling.

Why Use Docker?

Benefit

Description

πŸ“¦ Isolation

Each C-ITS station runs in its own container

πŸ”„ Reproducibility

Same environment on development and production

πŸ“ˆ Scalability

Easily spawn multiple stations with Docker Compose

🌐 Networking

Built-in virtual networks for V2X communication

πŸš€ Easy Deployment

Single command to start your entire V2X system


Architecture

        flowchart TB
    subgraph "Docker Host"
        subgraph "Container 1"
            FS1[FlexStack<br/>Station 1]
        end

        subgraph "Container 2"
            FS2[FlexStack<br/>Station 2]
        end

        subgraph "Container 3"
            FS3[FlexStack<br/>Station 3]
        end

        NET[Docker Network<br/>v2xnet]
    end

    FS1 <-->|"VAM/CAM"| NET
    FS2 <-->|"VAM/CAM"| NET
    FS3 <-->|"VAM/CAM"| NET

    style NET fill:#e3f2fd,stroke:#1565c0
    

Quick Start

Step 1: Create Project Structure

Create a directory with the following files:

my-v2x-project/
β”œβ”€β”€ app.py              # Your FlexStack application
β”œβ”€β”€ Dockerfile          # Container definition
└── docker-compose.yml  # Multi-container setup

Step 2: Create the Dockerfile

 1FROM python:3.9-slim
 2
 3WORKDIR /app
 4
 5# Install system dependencies
 6RUN apt-get update && apt-get install -y \
 7    gcc \
 8    python3-dev \
 9    build-essential \
10    && rm -rf /var/lib/apt/lists/*
11
12# Install FlexStack
13RUN pip install --no-cache-dir v2xflexstack
14
15# Copy application
16COPY app.py .
17
18# Run the application
19CMD ["python", "app.py"]

What this does:

Line

Description

FROM python:3.9-slim

Use lightweight Python 3.9 base image

apt-get install

Install C compiler for native extensions

pip install v2xflexstack

Install FlexStack package

CMD ["python", "app.py"]

Run your application on container start

Step 3: Create the Application

Create app.py β€” a VRU station that broadcasts VAMs:

  1#!/usr/bin/env python3
  2"""
  3FlexStack Docker Application
  4
  5A C-ITS VRU station that sends and receives VAMs.
  6"""
  7
  8import argparse
  9import logging
 10
 11from flexstack.facilities.vru_awareness_service.vru_awareness_service import (
 12    VRUAwarenessService,
 13)
 14from flexstack.facilities.vru_awareness_service.vam_transmission_management import (
 15    DeviceDataProvider,
 16)
 17from flexstack.utils.static_location_service import (
 18    ThreadStaticLocationService as LocationService,
 19)
 20from flexstack.btp.router import Router as BTPRouter
 21from flexstack.geonet.router import Router as GNRouter
 22from flexstack.geonet.mib import MIB
 23from flexstack.geonet.gn_address import GNAddress, M, ST, MID
 24from flexstack.linklayer.raw_link_layer import RawLinkLayer
 25
 26
 27def parse_mac_address(mac_str: str) -> bytes:
 28    """Convert MAC address string to bytes."""
 29    parts = mac_str.split(":")
 30    return bytes(int(x, 16) for x in parts)
 31
 32
 33def main():
 34    # Parse command line arguments
 35    parser = argparse.ArgumentParser(description="FlexStack C-ITS Station")
 36    parser.add_argument(
 37        "--station-id",
 38        type=int,
 39        default=1,
 40        help="Unique station identifier",
 41    )
 42    parser.add_argument(
 43        "--mac-address",
 44        type=str,
 45        default="aa:bb:cc:11:22:33",
 46        help="MAC address (e.g., aa:bb:cc:dd:ee:ff)",
 47    )
 48    parser.add_argument(
 49        "--interface",
 50        type=str,
 51        default="eth0",
 52        help="Network interface to use",
 53    )
 54    args = parser.parse_args()
 55
 56    logging.basicConfig(
 57        level=logging.INFO,
 58        format="[%(levelname)s] Station %(station_id)s: %(message)s",
 59        defaults={"station_id": args.station_id},
 60    )
 61    logger = logging.getLogger(__name__)
 62
 63    mac_address = parse_mac_address(args.mac_address)
 64
 65    # GeoNetworking
 66    mib = MIB(
 67        itsGnLocalGnAddr=GNAddress(
 68            m=M.GN_MULTICAST,
 69            st=ST.CYCLIST,
 70            mid=MID(mac_address),
 71        ),
 72    )
 73    gn_router = GNRouter(mib=mib, sign_service=None)
 74
 75    # Link Layer
 76    link_layer = RawLinkLayer(
 77        iface=args.interface,
 78        mac_address=mac_address,
 79        receive_callback=gn_router.gn_data_indicate,
 80    )
 81    gn_router.link_layer = link_layer
 82
 83    # BTP
 84    btp_router = BTPRouter(gn_router)
 85    gn_router.register_indication_callback(btp_router.btp_data_indication)
 86
 87    # Location Service
 88    location_service = LocationService()
 89    location_service.add_callback(gn_router.refresh_ego_position_vector)
 90
 91    # VRU Awareness Service
 92    device_data = DeviceDataProvider(
 93        station_id=args.station_id,
 94        station_type=2,  # Cyclist
 95    )
 96    vru_service = VRUAwarenessService(
 97        btp_router=btp_router,
 98        device_data_provider=device_data,
 99    )
100    location_service.add_callback(
101        vru_service.vam_transmission_management.location_service_callback
102    )
103
104    logger.info(f"οΏ½οΏ½ Station {args.station_id} started on {args.interface}")
105    logger.info(f"πŸ“‘ MAC: {args.mac_address}")
106    logger.info("Broadcasting VAMs...")
107
108    # Keep running
109    location_service.location_service_thread.join()
110
111
112if __name__ == "__main__":
113    main()

Running a Single Container

Build and Run

Build the Docker image:

docker build -t flexstack .

Run a single C-ITS station:

docker run --network host --privileged flexstack

Warning

The --network host and --privileged flags are required for raw socket access to network interfaces. This allows FlexStack to send/receive Ethernet frames.

With Custom Arguments

Override the station ID and MAC address:

docker run --network host --privileged flexstack \
    python app.py --station-id 42 --mac-address aa:bb:cc:dd:ee:ff

Running Multiple Stations

Use Docker Compose to run multiple C-ITS stations that communicate with each other.

Docker Compose Setup

Create docker-compose.yml:

 1version: '3.8'
 2
 3services:
 4  # First C-ITS Station (Cyclist)
 5  station1:
 6    build: .
 7    container_name: flexstack_station_1
 8    command: ["python", "app.py", "--station-id", "1", "--mac-address", "aa:bb:cc:11:22:01"]
 9    networks:
10      - v2xnet
11    cap_add:
12      - NET_ADMIN
13      - NET_RAW
14
15  # Second C-ITS Station (Cyclist)
16  station2:
17    build: .
18    container_name: flexstack_station_2
19    command: ["python", "app.py", "--station-id", "2", "--mac-address", "aa:bb:cc:11:22:02"]
20    networks:
21      - v2xnet
22    cap_add:
23      - NET_ADMIN
24      - NET_RAW
25
26  # Third C-ITS Station (Cyclist)
27  station3:
28    build: .
29    container_name: flexstack_station_3
30    command: ["python", "app.py", "--station-id", "3", "--mac-address", "aa:bb:cc:11:22:03"]
31    networks:
32      - v2xnet
33    cap_add:
34      - NET_ADMIN
35      - NET_RAW
36
37networks:
38  v2xnet:
39    driver: bridge

Key Configuration:

Setting

Purpose

networks: v2xnet

All stations share the same virtual network

cap_add: NET_ADMIN, NET_RAW

Required capabilities for raw socket access

command

Override default command with station-specific args

Start All Stations

Launch all stations:

docker-compose up

Or run in background:

docker-compose up -d

View logs:

docker-compose logs -f

Stop all stations:

docker-compose down

Communication Flow

When multiple stations run in Docker Compose, they communicate through the shared network:

        sequenceDiagram
    participant S1 as Station 1<br/>(Cyclist)
    participant NET as Docker Network<br/>(v2xnet)
    participant S2 as Station 2<br/>(Cyclist)
    participant S3 as Station 3<br/>(Cyclist)

    loop Every ~500ms
        S1->>NET: Broadcast VAM
        NET->>S2: Receive VAM
        NET->>S3: Receive VAM

        S2->>NET: Broadcast VAM
        NET->>S1: Receive VAM
        NET->>S3: Receive VAM

        S3->>NET: Broadcast VAM
        NET->>S1: Receive VAM
        NET->>S2: Receive VAM
    end
    

Scaling Stations

Easily scale to many stations:

# Run 5 instances of the station service
docker-compose up --scale station1=5

Or define a scalable service in docker-compose.yml:

services:
  cyclist:
    build: .
    networks:
      - v2xnet
    cap_add:
      - NET_ADMIN
      - NET_RAW
    deploy:
      replicas: 10

Advanced Configuration

Environment Variables

Use environment variables for configuration:

services:
  station:
    build: .
    environment:
      - STATION_ID=1
      - MAC_ADDRESS=aa:bb:cc:11:22:33
      - LOG_LEVEL=DEBUG

Then in your app.py:

import os

station_id = int(os.getenv("STATION_ID", "1"))
mac_address = os.getenv("MAC_ADDRESS", "aa:bb:cc:11:22:33")
log_level = os.getenv("LOG_LEVEL", "INFO")

Volume Mounting

Mount local files for development (changes reflect immediately):

services:
  station:
    build: .
    volumes:
      - ./app.py:/app/app.py
    networks:
      - v2xnet

Health Checks

Add health checks to monitor station status:

services:
  station:
    build: .
    healthcheck:
      test: ["CMD", "python", "-c", "import flexstack"]
      interval: 30s
      timeout: 10s
      retries: 3

Troubleshooting

Issue

Solution

Permission denied

Add --privileged flag or cap_add: NET_ADMIN, NET_RAW

Interface not found

Check interface name with ip link inside container

Stations not communicating

Ensure all stations are on the same Docker network

Build fails

Check that gcc and build-essential are installed

Debug Commands

Enter a running container:

docker exec -it flexstack_station_1 /bin/bash

Check network interfaces:

docker exec flexstack_station_1 ip link

View container logs:

docker logs -f flexstack_station_1

Complete Project

Here’s the complete project structure with all files:

my-v2x-project/
β”œβ”€β”€ app.py              # Application code (see Step 3)
β”œβ”€β”€ Dockerfile          # Container definition (see Step 2)
└── docker-compose.yml  # Multi-container setup

Quick commands:

# Build
docker-compose build

# Start all stations
docker-compose up -d

# View logs
docker-compose logs -f

# Stop everything
docker-compose down

See Also