# Docker on Bare Metal

This guide covers deploying Mermin as a Docker container on bare metal or virtual machines without Kubernetes. This is useful for monitoring standalone Linux hosts or environments where Kubernetes is not available.

{% hint style="warning" %}
Kubernetes metadata enrichment is not available in bare metal deployments. Flows will only contain network-level information (IPs, ports, protocols) without pod, service, or deployment metadata.
{% endhint %}

## Prerequisites

Before deploying on bare metal:

* **Linux OS**: RHEL/CentOS 7+, Ubuntu 18.04+, Debian 10+, or similar
* **Linux Kernel**: Version 5.14 or newer with eBPF and BTF support (6.6+ recommended)
* **Docker**: Version 19.03 or newer, or containerd/Podman as alternative
* **Root Access**: Required to run privileged containers
* **Network Access**: To OTLP collector endpoint

### Verify eBPF Support

Check that your kernel supports eBPF:

```bash
# Check kernel version
uname -r
# Should be >= 4.18

# Verify CONFIG_BPF is enabled
grep CONFIG_BPF /boot/config-$(uname -r)
# Should show: CONFIG_BPF=y

# Check for BPF filesystem
mount | grep bpf
# Should show: bpffs on /sys/fs/bpf type bpf
```

If `bpffs` is not mounted:

```bash
sudo mount -t bpf bpf /sys/fs/bpf
```

## Configuration

Create a Mermin configuration file optimized for bare metal:

```hcl
# mermin-baremental.hcl

# Logging configuration
log_level = "info"

# Shutdown timeout
shutdown_timeout = "28s"

# Pipeline configuration
pipeline {
  flow_producer {
    workers = 4
    worker_queue_capacity = 2048
    flow_span_queue_capacity = 16384
  }
}

# Network interfaces to monitor
discovery "instrument" {
  # Adjust interface names for your system
  # Use: ip link show
  interfaces = ["eth0", "ens*"]
}

# Flow span configuration
span {
  max_record_interval = "60s"
  generic_timeout = "30s"
  icmp_timeout = "10s"
  tcp_timeout = "20s"
  tcp_fin_timeout = "5s"
  tcp_rst_timeout = "5s"
  udp_timeout = "60s"
  community_id_seed = 0
}

# OTLP exporter configuration
export "traces" {
  # For testing: output to stdout
  stdout = "text_indent"

  # For production: send to OTLP collector
  # otlp = {
  #   endpoint = "http://collector.example.com:4317"
  #   protocol = "grpc"
  #   timeout = "10s"
  #   max_batch_size = 512
  #   max_batch_interval = "5s"
  #   max_queue_size = 2048
  # }
}

# HTTP server (health checks)
internal "server" {
  enabled = true
  listen_address = "0.0.0.0"
  port = 8080
}

# Metrics server (Prometheus)
internal "metrics" {
  enabled = true
  listen_address = "0.0.0.0"
  port = 10250
}

# Parser configuration
parser {
  geneve_port = 6081
  vxlan_port = 4789
  wireguard_port = 51820
}
```

## Deployment with Docker

### Pull the Image

```bash
docker pull ghcr.io/elastiflow/mermin:latest
```

### Run Mermin Container

Run Mermin with necessary privileges and volume mounts:

```bash
docker run -d \
  --name mermin \
  --privileged \
  --network host \
  --pid host \
  --cap-add SYS_ADMIN \
  --cap-add SYS_PTRACE \
  --cap-add NET_ADMIN \
  --cap-add BPF \
  -v /sys/kernel/debug:/sys/kernel/debug:ro \
  -v /sys/fs/bpf:/sys/fs/bpf \
  -v $(pwd)/mermin-baremetal.hcl:/etc/mermin/config.hcl:ro \
  ghcr.io/elastiflow/mermin:latest \
  --config /etc/mermin/config.hcl
```

**Flags explained:**

* `--privileged`: Required for eBPF program loading
* `--network host`: Access host network interfaces
* `--pid host`: Access host process information (optional)
* `--cap-add`: Explicit capabilities for eBPF and networking
* `-v /sys/kernel/debug`: Debug filesystem for eBPF (read-only)
* `-v /sys/fs/bpf`: BPF filesystem for program management
* `-v config.hcl`: Mount configuration file

### Verify Deployment

Check that the container is running:

```bash
docker ps | grep mermin
```

View logs:

```bash
docker logs mermin -f
```

Check health:

```bash
curl http://localhost:8080/livez
curl http://localhost:8080/readyz
```

Both should return `ok`.

## Deployment with Systemd

For production deployments, use systemd to manage the container:

### Create Systemd Service

```bash
sudo nano /etc/systemd/system/mermin.service
```

```ini
[Unit]
Description=Mermin Network Observability Agent
After=docker.service
Requires=docker.service

[Service]
Type=simple
Restart=always
RestartSec=10
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker stop mermin
ExecStartPre=-/usr/bin/docker rm mermin
ExecStartPre=/usr/bin/docker pull ghcr.io/elastiflow/mermin:latest
ExecStart=/usr/bin/docker run --rm \
  --name mermin \
  --privileged \
  --network host \
  --pid host \
  --cap-add SYS_ADMIN \
  --cap-add SYS_PTRACE \
  --cap-add NET_ADMIN \
  --cap-add BPF \
  -v /sys/kernel/debug:/sys/kernel/debug:ro \
  -v /sys/fs/bpf:/sys/fs/bpf \
  -v /etc/mermin/config.hcl:/etc/mermin/config.hcl:ro \
  ghcr.io/elastiflow/mermin:latest \
  --config /etc/mermin/config.hcl
ExecStop=/usr/bin/docker stop mermin

[Install]
WantedBy=multi-user.target
```

### Enable and Start Service

```bash
# Create config directory
sudo mkdir -p /etc/mermin
sudo cp mermin-baremetal.hcl /etc/mermin/config.hcl

# Reload systemd
sudo systemctl daemon-reload

# Enable service to start on boot
sudo systemctl enable mermin

# Start service
sudo systemctl start mermin

# Check status
sudo systemctl status mermin

# View logs
sudo journalctl -u mermin -f
```

## Deployment with Podman

Podman is a daemonless alternative to Docker:

```bash
# Run with Podman (similar flags as Docker)
podman run -d \
  --name mermin \
  --privileged \
  --network host \
  --pid host \
  --cap-add SYS_ADMIN \
  --cap-add SYS_PTRACE \
  --cap-add NET_ADMIN \
  --cap-add BPF \
  -v /sys/kernel/debug:/sys/kernel/debug:ro \
  -v /sys/fs/bpf:/sys/fs/bpf \
  -v $(pwd)/mermin-baremetal.hcl:/etc/mermin/config.hcl:ro \
  ghcr.io/elastiflow/mermin:latest \
  --config /etc/mermin/config.hcl

# Generate systemd service unit
podman generate systemd --name mermin --files --new

# Move service file and enable
sudo mv container-mermin.service /etc/systemd/system/mermin.service
sudo systemctl daemon-reload
sudo systemctl enable mermin
sudo systemctl start mermin
```

## Configuration for Bare Metal

### Identifying Network Interfaces

List available interfaces:

```bash
ip link show
```

Common interface naming:

* **Traditional**: `eth0`, `eth1`
* **Predictable**: `ens32`, `eno1`, `enp0s3`
* **Virtual**: `docker0`, `veth*`, `br-*`

Update your configuration:

```hcl
discovery "instrument" {
  # Monitor primary interface
  interfaces = ["ens32"]

  # Or use glob patterns
  # interfaces = ["eth*", "ens*"]
}
```

### Multi-Host Deployments

Deploy Mermin on multiple hosts for fleet-wide observability:

**Host 1:**

```hcl
export "traces" {
  otlp = {
    endpoint = "http://central-collector.example.com:4317"
    protocol = "grpc"

    # Add host identifier
    resource_attributes = {
      "host.name" = "web-server-01"
      "host.ip" = "192.168.1.10"
      "host.role" = "webserver"
    }
  }
}
```

**Host 2:**

```hcl
export "traces" {
  otlp = {
    endpoint = "http://central-collector.example.com:4317"
    protocol = "grpc"

    resource_attributes = {
      "host.name" = "db-server-01"
      "host.ip" = "192.168.1.20"
      "host.role" = "database"
    }
  }
}
```

## Monitoring and Logs

### View Real-Time Logs

```bash
# Docker
docker logs mermin -f --tail 100

# Systemd
sudo journalctl -u mermin -f -n 100
```

### Access Metrics

```bash
# Prometheus metrics
curl http://localhost:10250/metrics

# Or use port forwarding if needed
ssh -L 10250:localhost:10250 user@remote-host
# Then access http://localhost:10250/metrics locally
```

### Log Rotation

Configure Docker log rotation in `/etc/docker/daemon.json`:

```json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "5"
  }
}
```

Restart Docker:

```bash
sudo systemctl restart docker
sudo systemctl restart mermin
```

## Limitations Compared to Kubernetes

Bare metal deployments have these limitations:

| Feature               | Kubernetes  | Bare Metal      |
| --------------------- | ----------- | --------------- |
| **Pod Metadata**      | ✅ Full      | ❌ Not Available |
| **Service Mapping**   | ✅ Yes       | ❌ No            |
| **Owner References**  | ✅ Yes       | ❌ No            |
| **Network Policies**  | ✅ Yes       | ❌ No            |
| **Auto-Discovery**    | ✅ Informers | ❌ Manual Config |
| **Flow Traces**       | ✅ Available | ✅ Available     |
| **Protocol Analysis** | ✅ Available | ✅ Available     |

Bare metal deployments capture raw network flows without Kubernetes context.

## Use Cases for Bare Metal

Bare metal deployments are suitable for:

* **Non-Kubernetes Environments**: Traditional VMs or physical servers
* **Host-Level Monitoring**: Monitor host OS network activity
* **Hybrid Environments**: Bridge Kubernetes and non-Kubernetes infrastructure
* **Edge Deployments**: Lightweight observability at edge locations
* **Testing and Development**: Quick setup for experimentation

## Troubleshooting

### Container Exits Immediately

Check logs for errors:

```bash
docker logs mermin
```

Common causes:

* Missing configuration file
* Invalid configuration syntax
* Interface not found

### "Operation not permitted" Errors

Ensure container has necessary privileges:

```bash
docker run --privileged \
  --cap-add SYS_ADMIN \
  --cap-add SYS_PTRACE \
  --cap-add NET_ADMIN \
  --cap-add BPF \
  ...
```

### No Flow Traces

Check that interfaces exist:

```bash
docker exec mermin ip link show
```

Verify eBPF programs are loaded:

```bash
docker exec mermin ls /sys/fs/bpf/
```

### High CPU Usage

Reduce monitored interfaces:

```hcl
discovery "instrument" {
  # Monitor only specific interface
  interfaces = ["eth0"]
}
```

Increase flow timeouts:

```hcl
span {
  generic_timeout = "60s"
  tcp_timeout = "30s"
  udp_timeout = "120s"
}
```

## Updating Mermin

### Docker

```bash
# Stop and remove old container
docker stop mermin
docker rm mermin

# Pull new image
docker pull ghcr.io/elastiflow/mermin:latest

# Start with same configuration
docker run -d ... [same flags as before]
```

### Systemd

```bash
# Service will automatically pull latest on restart
sudo systemctl restart mermin

# Or manually
sudo systemctl stop mermin
docker pull ghcr.io/elastiflow/mermin:latest
sudo systemctl start mermin
```

## Best Practices

1. **Always use systemd**: For production deployments
2. **Configure log rotation**: Prevent disk filling
3. **Monitor resource usage**: Set up alerts on CPU/memory
4. **Use configuration management**: Ansible, Puppet, or Chef for fleet deployments
5. **Secure OTLP connections**: Use TLS and authentication
6. **Test configuration**: Validate before rolling out to production
7. **Document host identifiers**: Maintain inventory of monitored hosts

## Next Steps

{% tabs %}
{% tab title="Configure Export" %}

1. [**Secure Your OTLP Connection**](/configuration/reference/opentelemetry-otlp-exporter.md): Set up TLS and authentication
2. [**Connect to Your Backend**](/getting-started/backend-integrations.md): Send Flow Traces to your observability platform
   {% endtab %}

{% tab title="Optimize" %}

1. [**Fine-Tune Configuration**](/configuration/overview.md): Optimize for bare metal environments
2. [**Filter Flows Before Export**](/configuration/reference/flow-span-filters.md): Reduce noise and storage costs
   {% endtab %}

{% tab title="Need Kubernetes Metadata?" %}
[**Switch to Kubernetes Deployment**](/deployment/kubernetes-helm.md)

Get full pod, service, and deployment metadata enrichment by deploying to Kubernetes.
{% endtab %}
{% endtabs %}

### Need Help?

* [**Troubleshoot Common Issues**](/troubleshooting/troubleshooting.md): Resolve deployment and eBPF errors
* [**GitHub Discussions**](https://github.com/elastiflow/mermin/discussions): Ask questions and share experiences


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mermin.dev/deployment/docker-bare-metal.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
