Skip to main content

Deploy to Your First Edge Node

In the Getting Started tutorial, you ran both the orchestrator and edge node on your local machine. That's perfect for learning the basics, but production edge computing is different, and you'll deploy jobs to remote machines with real network challenges, firewall rules, and intermittent connectivity.

In this tutorial, you'll set up a real edge node on a separate Linux server or VM, configure it to connect to Expanso Cloud across a network, and deploy a data processing job that continues working even when network connectivity is disrupted. By the end, you'll understand how Expanso's edge architecture handles the realities of distributed computing.

This tutorial takes about 30-40 minutes to complete.

Deploying as a Kubernetes Sidecar

If you're running workloads in Kubernetes, you can deploy Expanso Edge as a sidecar container alongside your existing application pods. This approach leverages standard Kubernetes patterns to collect logs, metrics, or other telemetry without managing separate edge servers.

:::note Kubernetes Sidecar Example Replace the placeholder values:

  • YOUR_BOOTSTRAP_TOKEN: a bootstrap token created in the Expanso Cloud console (Keys page).
  • your-app-image: your application container image.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: your-app-image:latest
- name: expanso-edge
image: ghcr.io/expanso-io/expanso-edge:latest
args: ["run"]
env:
- name: EXPANSO_EDGE_BOOTSTRAP_TOKEN
value: YOUR_BOOTSTRAP_TOKEN
volumeMounts:
- name: expanso-cache
mountPath: /var/lib/expanso
volumes:
- name: expanso-cache
emptyDir: {}

:::

:::tip Kubernetes Logging Best Practices

  • Prefer stdout/stderr: Kubernetes captures container stdout/stderr logs by default. Emitting logs there integrates with cluster logging drivers.
  • Use a shared volume for file logs: If your app writes logs to files, mount them on an emptyDir (or hostPath for host-level logs) and point Expanso at those paths. :::

What You'll Learn

  • How to prepare a remote Linux machine as an edge node
  • How to install and configure the Expanso edge binary on a remote system
  • How to set up network connectivity and authentication between nodes and your workspace
  • How to use node labels for targeted job deployment
  • How to monitor edge node health and connectivity
  • How to test and verify autonomous operation during network issues
  • How to troubleshoot common connectivity problems

Prerequisites

Before starting, make sure you have:

  • An Expanso Cloud account and workspace (sign up at cloud.expanso.io)
  • A separate Linux machine or VM for the edge node (Ubuntu 20.04+, Debian 11+, or similar)
  • SSH access to the remote machine
  • Outbound network connectivity from the edge node to Expanso Cloud
  • Basic familiarity with Linux command line
  • Firewall configuration access (if applicable)

:::tip Cloud or Physical? You can use a cloud VM (AWS EC2, GCP Compute, Azure VM) or a physical server. The steps are identical. Expanso Edge has a minimal footprint and runs on virtually any modern hardware. :::

Step 1: Prepare Your Edge Machine

First, let's prepare your remote machine for Expanso.

SSH into your edge machine:

Verify system compatibility:

# Verify Linux kernel version (3.10+ required)
uname -r

# Check available disk space for buffering
df -h /var/lib

# Verify network connectivity to your workspace
ping cloud.expanso.io

Create the Expanso data directory:

sudo mkdir -p /var/lib/expanso
sudo mkdir -p /etc/expanso
sudo chown $USER:$USER /var/lib/expanso /etc/expanso

This directory will store pipeline configurations, state, and buffered data when the node operates offline.

:::info Why /var/lib/expanso? Following Linux filesystem hierarchy standards, /var/lib/expanso stores variable application data that persists across reboots. This is where buffered messages, local state, and temporary pipeline data live during normal operation and network outages. :::

Step 2: Install the Expanso Edge Binary

Now let's install the edge node software on your remote machine.

Install the edge agent:

curl -fsSL https://get.expanso.io/edge/install.sh | sudo bash

Verify the installation:

expanso-edge version

You should see output like:

Expanso Edge <version>

Create a systemd service (optional but recommended for production):

Download the service file:

sudo curl -o /etc/systemd/system/expanso-edge.service https://docs.expanso.io/examples/deployment/expanso-edge.service
sudo systemctl daemon-reload

Or view the service file

We'll configure and start this service shortly, but having it defined now means your edge node will automatically restart if the machine reboots.

Step 3: Configure Network Connectivity

Edge nodes connect outbound to Expanso Cloud: HTTPS on port 443 to cloud.expanso.io to fetch bootstrap credentials, then NATS on port 4222 to your workspace. Expanso Cloud hosts your workspace, so there is nothing to configure on the workspace side. You only need to make sure the edge node can reach Expanso Cloud.

Test connectivity from the edge node:

# Reach the bootstrap endpoint over HTTPS
nc -zv cloud.expanso.io 443

# You should see:
# Connection to cloud.expanso.io 443 port [tcp/*] succeeded!

The agent also needs outbound access to your workspace's NATS endpoint on port 4222.

:::caution Cloud Security Groups If your edge node runs on a cloud VM, make sure its security groups or network ACLs allow outbound connections to Expanso Cloud on ports 443 and 4222. No inbound ports need to be opened on the edge node. :::

Step 4: Create a Bootstrap Token

Edge nodes authenticate to your workspace using a bootstrap token during initial registration. Bootstrap tokens are short-lived and are created in Expanso Cloud, not from the CLI.

Create a token in the Expanso Cloud console:

  1. Open the Expanso Cloud console and go to the Keys page.
  2. Create a new bootstrap token. Give it a description (for example, "Production edge node in Seattle datacenter") so you can identify it later.
  3. Copy the token value. It looks like this:
exp_bk_a4b5c6d7e8f9g0h1i2j3k4l5

You'll only see the token value once, so copy it now, since you'll pass it to the agent in the next step.

:::info Token Security Bootstrap tokens are short-lived and can be revoked. A single token can register one or more nodes while it is valid. Once a node registers successfully, it receives long-term credentials and no longer needs the bootstrap token. Keeping the validity window short limits the blast radius if a token is compromised. :::

Step 5: Configure the Edge Node

Now let's create a configuration file for your edge node with node identity, labels, and workspace connection details.

Create the edge configuration file:

Download the template configuration:

sudo curl -o /etc/expanso/edge-config.yaml https://docs.expanso.io/examples/deployment/edge-config.yaml

Or view the configuration file

Important: Edit the file to customize:

  • name: Your edge node's name (optional; defaults to the hostname)
  • labels: Labels for job targeting (region, datacenter, environment, etc.)

Let's break down the key sections:

Node identity: The name is human-friendly but doesn't need to be globally unique. Your workspace will assign a unique ID during registration. The labels are crucial, since you'll use these to target jobs to specific nodes.

Workspace connection: You don't configure a workspace address. During bootstrap the edge node registers with Expanso Cloud and automatically receives its workspace endpoint and credentials. You only provide the bootstrap token, as a CLI flag or environment variable (not in the config file).

Data directory: The data_dir stores pipeline state and buffers messages during network outages.

:::info Bootstrap Token Handling Bootstrap tokens should be provided via CLI flag (--bootstrap-token) or environment variable (EXPANSO_EDGE_BOOTSTRAP_TOKEN), not in configuration files. This ensures tokens are never accidentally committed to version control or logged. :::

Step 6: Start the Edge Node

With configuration in place, let's start the edge node and verify it connects successfully.

Start the edge node:

Pass the bootstrap token from Step 4 using the --bootstrap-token flag:

expanso-edge run \
--config=/etc/expanso/edge-config.yaml \
--bootstrap-token=exp_bk_a4b5c6d7e8f9g0h1i2j3k4l5

Or use an environment variable:

export EXPANSO_EDGE_BOOTSTRAP_TOKEN=exp_bk_a4b5c6d7e8f9g0h1i2j3k4l5
expanso-edge run --config=/etc/expanso/edge-config.yaml

Watch the output as the agent starts. It connects to your workspace over NATS, presents the bootstrap token, and registers. Once registration succeeds, the node receives long-term credentials in place of the bootstrap token, and the agent begins reporting a healthy state. The output resembles:

INFO connecting to orchestrator over NATS
INFO registering node with bootstrap token
INFO registration complete, node connected and ready

Perfect! Your edge node is now registered and connected. Let's verify it registered using the CLI.

In a new terminal on your local machine, list nodes with the management CLI:

expanso-cli node list

You should see your new edge node. node list prints these columns: ID, NAME, STATE, VERSION, LABELS, and RESOURCES:

ID NAME STATE VERSION LABELS RESOURCES
node-a1b2c3d4e5f6 edge-seattle-01 connected <version> datacenter=seattle,environment=production,... 4 CPU, 8 GB

Get detailed node information:

expanso-cli node describe node-a1b2c3d4e5f6

node describe prints four sections: NODE DETAILS, SYSTEM INFORMATION, CAPABILITIES, and LABELS:

NODE DETAILS
ID: node-a1b2c3d4e5f6
Name: edge-seattle-01
State: connected
Version: <version>

SYSTEM INFORMATION
OS: linux
Architecture: amd64
CPU Cores: 4
Memory: 8192 MB

CAPABILITIES
Ready to run pipeline inputs, processors, and outputs

LABELS
datacenter=seattle
environment=production
hardware=cpu
region=us-west

The State field and the LABELS section are what you'll rely on most: State tells you the node is reachable and healthy, and the labels are what job selectors match against. Your edge node is fully operational.

:::tip Systemd Service For production deployments, use the systemd service we created earlier. Stop the foreground process (Ctrl+C) and start the service:

sudo systemctl enable expanso-edge
sudo systemctl start expanso-edge
sudo systemctl status expanso-edge

This ensures the edge node starts automatically on boot and restarts if it crashes. :::

Step 7: Deploy a Job to the Edge Node

Now let's deploy a real data processing job that targets your specific edge node using labels.

Create a job that processes syslog messages:

Download the example job configuration:

curl -o edge-syslog-processor.yaml https://docs.expanso.io/examples/deployment/edge-syslog-processor.yaml

Or view the job configuration

Important: Edit the file to customize:

  • spec.selector.match_labels: Adjust to match your node labels
  • config.output.http_client.url: Your logging ingestion endpoint
  • config.output.http_client.headers.Authorization: Your authentication token

This job does several important things:

  • Selective targeting: Only deploys to nodes with labels region=us-west AND environment=production
  • Resilient processing: Reads local syslog, parses structured data, and filters for important messages
  • Dual output: Sends to central logging but also buffers locally if the network is down
  • Edge enrichment: Adds node identity to messages, which is crucial for multi-site deployments

Deploy the job:

expanso-cli job deploy edge-syslog-processor.yaml

The CLI confirms that the syslog-processor job was created and scheduled to the matching node (node-a1b2c3d4e5f6).

Verify the job is running on your edge node:

expanso-cli job executions syslog-processor

The output shows one execution of syslog-processor running on edge-seattle-01 (node-a1b2c3d4e5f6). The pipeline is now processing syslog messages on your edge node in real-time!

To follow the pipeline's logs, use:

expanso-cli job logs syslog-processor

Step 8: Monitor Edge Node Connectivity

One of Expanso's key features is handling network disruptions gracefully. Your workspace tracks each node's connectivity through periodic heartbeats and reflects the result in the node's STATE.

Check current state:

The fastest way to see how your fleet is doing is the STATE column in node list:

expanso-cli node list

Look at a single node in detail:

expanso-cli node describe node-a1b2c3d4e5f6

The State field reflects the node's current connectivity: connected when your workspace is receiving heartbeats, disconnected after heartbeats stop arriving, and lost after an extended timeout without heartbeats. There's no separate event-stream or connection command. node list and node describe are how you observe status.

:::tip Production Monitoring In production, poll expanso-cli node list on a schedule and alert when any node's STATE leaves connected/healthy. This gives you fast notification of connectivity problems without needing to watch individual nodes. :::

Step 9: Test Network Partition Scenarios

Now let's simulate real-world network issues and verify that your edge node continues operating autonomously.

Scenario 1: Brief Network Interruption

Temporarily block network connectivity from your edge node:

# On the edge machine
sudo iptables -A OUTPUT -p tcp --dport 4222 -j DROP

Watch what happens:

From your workspace, poll the node's state:

expanso-cli node describe node-a1b2c3d4e5f6

As heartbeats start to be missed, the node's State moves from connected to disconnected, and after an extended timeout without heartbeats it becomes lost. But here's the critical part: the pipeline keeps running on the edge node. The local buffering mechanism activates, storing processed syslog messages under the node's data directory until connectivity returns.

Restore connectivity:

# On the edge machine
sudo iptables -D OUTPUT -p tcp --dport 4222 -j DROP

Within a short time, the next node describe shows the State returning to connected/healthy. This was a brief interruption, so the node keeps the same session, it didn't restart, it reconnected. The buffered messages now flush to the central logging system.

Scenario 2: Extended Network Outage

For longer outages, the edge node starts a new session when connectivity returns. Let's test this:

# On the edge machine, block connectivity for 2 minutes
sudo iptables -A OUTPUT -p tcp --dport 4222 -j DROP
sleep 120
sudo iptables -D OUTPUT -p tcp --dport 4222 -j DROP

When the node reconnects after a prolonged disconnection, it begins a new session while keeping its existing node ID. Poll expanso-cli node describe node-a1b2c3d4e5f6 and you'll see the State return to connected/healthy. Your workspace resynchronizes state and verifies that the job is still running correctly.

:::info Session Continuity Edge nodes maintain their identity (node ID) across sessions. Sessions are logical operational periods, not tied to process lifetime. This design allows the system to track and correlate events while handling network realities. :::

Step 10: Verify Autonomous Operation

Let's verify that your edge node truly operates independently during network outages.

Check the local buffer during an outage:

The example pipeline is configured to fall back to a local directory when its remote destination is unreachable. While connectivity is blocked, SSH to your edge node and inspect that fallback directory (in this example, under the data directory at /var/lib/expanso/buffer/):

# On the edge machine
ls -lh /var/lib/expanso/buffer/

You'll see buffered data files accumulating during the outage. View their contents:

tail -5 /var/lib/expanso/buffer/syslog-*.jsonl

You'll see properly formatted, processed syslog entries, enriched with node identity by the pipeline:

{"timestamp":"Oct 20 16:05:45","hostname":"edge-seattle-01","program":"systemd","message":"Started Daily apt download activities.","node_id":"node-a1b2c3d4e5f6","node_hostname":"edge-seattle-01","ingested_at":"2025-10-20T16:05:45Z"}
{"timestamp":"Oct 20 16:06:02","hostname":"edge-seattle-01","program":"kernel","message":"warning: CPU throttling detected","node_id":"node-a1b2c3d4e5f6","node_hostname":"edge-seattle-01","ingested_at":"2025-10-20T16:06:02Z"}

The pipeline continued processing data locally, even without workspace connectivity!

Watch the buffer flush after reconnection:

When connectivity returns, watch the buffer directory:

watch -n 2 'ls -lh /var/lib/expanso/buffer/ | tail -5'

You'll see files disappear as messages are sent to the central logging system. This demonstrates Expanso's edge-first architecture: process data locally, sync when possible, never lose data.

Verification Checklist

Let's verify everything is working correctly:

  • ✅ Edge node is installed on a separate Linux machine
  • ✅ Network connectivity to your workspace is configured (firewall, security groups)
  • ✅ Edge node successfully registered using a bootstrap token
  • ✅ Long-term credentials are stored and the bootstrap token is consumed
  • ✅ Node appears with a connected/healthy STATE in expanso-cli node list
  • ✅ Labels are correctly configured and visible in expanso-cli node describe
  • ✅ Job deployed successfully to edge node based on label selectors
  • ✅ Pipeline is processing data (syslog messages)
  • ✅ Node STATE stays connected/healthy under normal operation
  • ✅ Network interruptions are handled gracefully (state moves to disconnected, then lost)
  • ✅ Data buffers locally during outages
  • ✅ Buffered data flushes when connectivity returns
  • ✅ Node reconnects automatically after network issues

If all items are checked, congratulations! You have a production-ready edge deployment.

What You Learned

You've accomplished a lot in this tutorial:

  • ✅ Set up a production edge node on a remote Linux machine
  • ✅ Configured network connectivity and firewall rules between your workspace and the edge node
  • ✅ Used a bootstrap token for secure initial registration
  • ✅ Configured node labels for targeted job deployment
  • ✅ Deployed a real-world data processing pipeline (syslog processing)
  • ✅ Monitored node health and connectivity via node state
  • ✅ Tested network partition scenarios and verified autonomous operation
  • ✅ Confirmed local buffering during outages and synchronization on recovery

Key Concepts

Bootstrap Tokens: Short-lived credentials for initial node registration, created on the Keys page in the Expanso Cloud console. A token can register one or more nodes while it is valid. After registration, nodes receive long-term credentials that don't expire.

Node Labels: Key-value pairs attached to nodes (like region=us-west, environment=production) used by job selectors to control where jobs run. Labels are metadata, not security boundaries.

Heartbeats: Periodic health reports from edge nodes to your workspace. Your workspace uses heartbeats to track connectivity and update each node's STATE.

Session Continuity: Edge nodes maintain logical sessions that persist across brief network interruptions. A new session starts after an extended outage or a process restart, but the node identity remains constant.

Local Buffering: When network connectivity is lost, edge nodes buffer processed data locally. When connectivity returns, buffered data automatically syncs to remote destinations.

Autonomous Operation: Edge nodes continue processing data during network outages without workspace connectivity. Your workspace tracks desired state but doesn't need to be reachable for pipelines to function.

:::tip Deep Dive Want to understand the architecture behind these features? Read: :::

Next Steps

Now that you have a production edge deployment, here's where to go next:

Scale Your Deployment:

Advanced Job Configuration:

Production Operations:

Architecture Deep Dives:

Troubleshooting

Edge Node Can't Connect to Your Workspace

Symptom: Edge node logs show connection errors or timeouts.

Diagnosis:

# On edge node, test connectivity
nc -zv cloud.expanso.io 443

# Check DNS resolution
nslookup cloud.expanso.io

# Verify routing
traceroute cloud.expanso.io

Common Causes:

  1. Firewall blocking traffic: Verify firewall rules allow outbound TCP on port 4222
  2. Security groups (cloud): Check cloud security group rules
  3. DNS issues: Verify hostname resolves correctly
  4. NAT/routing: Ensure a network route exists between the edge node and your workspace

Solution:

# From the edge node, confirm it can reach Expanso Cloud
nc -zv cloud.expanso.io 443

# Verify DNS resolves
getent hosts cloud.expanso.io

# If the edge node has an outbound firewall, allow outbound TCP to Expanso Cloud on ports 443 and 4222

Bootstrap Token Authentication Failed

Symptom: Node logs show "invalid bootstrap token" or "token expired".

Diagnosis:

Open the Keys page in the Expanso Cloud console and check the token you used:

  1. Confirm it hasn't expired (bootstrap tokens are short-lived).
  2. Confirm it hasn't been revoked, and that it hasn't reached its use limit if one was set when the token was created.

Common Causes:

  1. Token expired: Bootstrap tokens have short lifespans
  2. Token revoked or at its use limit: the token was revoked, or it reached a maximum number of uses set when it was created
  3. Token typo: Copy-paste errors when passing the token to the agent

Solution:

Create a fresh bootstrap token on the Keys page, then restart the agent with the new token:

# Restart with the new token via flag...
expanso-edge run \
--config=/etc/expanso/edge-config.yaml \
--bootstrap-token=exp_bk_NEW_TOKEN_VALUE

# ...or via environment variable, then restart the service
export EXPANSO_EDGE_BOOTSTRAP_TOKEN=exp_bk_NEW_TOKEN_VALUE
sudo systemctl restart expanso-edge

Node Shows "Offline" Despite Running

Symptom: Edge node process is running but your workspace shows the node's STATE as "offline".

Diagnosis:

# Check edge node logs
sudo journalctl -u expanso-edge -f

# Check the node's state with the CLI
expanso-cli node describe <node-id>

# Check network connectivity
nc -zv cloud.expanso.io 443

Common Causes:

  1. Heartbeat timeout: Network latency causing heartbeats to arrive late
  2. Clock skew: System clocks out of sync between the edge node and your workspace
  3. NATS connection issues: TLS handshake failures

Solution:

# Sync system clock (edge node)
sudo timedatectl set-ntp true

# Check TLS certificate validity (if using custom CA)
openssl s_client -connect cloud.expanso.io:443

sudo systemctl restart expanso-edge

Job Deployed But Not Running on Edge Node

Symptom: Job shows as deployed but execution status is "pending" or "failed".

Diagnosis:

# Check job executions
expanso-cli job executions <job-name>

# View the pipeline's logs
expanso-cli job logs <job-name>

# Verify node capabilities and labels
expanso-cli node describe <node-id>

Common Causes:

  1. Label mismatch: Job selector doesn't match node labels
  2. Resource constraints: Node doesn't have required CPU/memory
  3. Missing dependencies: Job requires unsupported inputs/outputs
  4. Configuration errors: Invalid pipeline configuration

Solution:

# Verify label matching (node list shows LABELS by default)
expanso-cli node list

# Review the job's selector (job describe shows the selector by default)
expanso-cli job describe <job-name>

# Check execution details for errors
expanso-cli execution describe <execution-id>

# View agent logs on edge node
sudo journalctl -u expanso-edge -f | grep <job-name>

# Validate job configuration locally
expanso-cli job validate <job-file.yaml>

Buffered Data Not Flushing After Reconnection

Symptom: Buffer files remain on disk after connectivity returns.

Diagnosis:

# Check buffer directory (on edge node)
ls -lh /var/lib/expanso/buffer/

# Verify output destination is reachable
curl -I https://logs.example.com/ingest

# Check pipeline logs
sudo journalctl -u expanso-edge -f | grep fallback

Common Causes:

  1. Destination unreachable: Remote endpoint still down
  2. Authentication issues: Credentials expired or invalid
  3. Rate limiting: Remote service throttling requests
  4. Disk full: No space for temporary files during flush

Solution:

# Test destination manually
curl -X POST https://logs.example.com/ingest \
-H "Authorization: Bearer $TOKEN" \
-d '{"test": "message"}'

# Check disk space
df -h /var/lib/expanso

# Manually trigger flush (restart edge service)
sudo systemctl restart expanso-edge

# Increase flush rate in config if destination can handle it
# config.output.http_client.max_in_flight: 50

Need More Help?

If you're still experiencing issues:

  1. Check the edge node logs: Detailed logs are your best diagnostic tool

    sudo journalctl -u expanso-edge -n 100

    Your workspace is managed by Expanso Cloud. If the problem looks like it is on the workspace side, check the Cloud console or contact support.

  2. Enable debug logging: Temporarily increase verbosity

    # /etc/expanso/edge/config.yaml
    log:
    level: debug
  3. Community support:

  4. File a bug report:

    • Include logs from both your workspace and the edge node
    • Describe steps to reproduce
    • Share configuration files (redact secrets)