A Raspberry Pi used as a portable server or remote-management node often needs more than one way to reach the network. A practical design may include three independent uplinks:

  • wired Ethernet,
  • local Wi-Fi,
  • a USB cellular or tethering device.

The challenge is not merely getting all three interfaces online. The system should also have a predictable priority order, survive reboots, tolerate changing USB interface names, and remain remotely reachable even when no known Wi-Fi network is available.

This article describes a robust design based on:

Ethernet
   ↓
Wi-Fi
   ↓
USB cellular

with automatic route selection and a stable logical name for the cellular interface.

All identifying information below has been replaced with generic placeholders.


1. Target Architecture

The desired behavior is:

                ┌──────────────────────┐
                │    Raspberry Pi      │
                │                      │
                │  eth0     metric 100 │
                │  wlan0    metric 200 │
                │  cell0    metric 300 │
                └──────────┬───────────┘
                           │
              lowest metric wins
                           │
          ┌────────────────┼────────────────┐
          │                │                │
       Ethernet          Wi-Fi        USB cellular
       preferred        secondary       fallback

The final priority is:

InterfacePurposeMetricPriority
eth0Wired Ethernet100Highest
wlan0Local Wi-Fi200Second
cell0USB cellular/tethering300Last fallback

Linux normally prefers the route with the lowest metric when several default routes are available.

Therefore:

100 < 200 < 300

means:

Ethernet → Wi-Fi → Cellular

2. Identifying the Actual Network Stack

A common mistake on modern Ubuntu systems is to assume that NetworkManager or systemd-networkd is managing the interfaces.

That assumption should always be verified.

A system may instead be using:

dhcpcd
+
wpa_supplicant

The roles are different.

dhcpcd

dhcpcd handles:

  • DHCP address acquisition,
  • gateways,
  • routing,
  • route metrics,
  • DNS information,
  • interface lifecycle.

wpa_supplicant

wpa_supplicant handles:

  • Wi-Fi authentication,
  • WPA/WPA2/WPA3 association,
  • connection to an access point.

A typical diagnostic may reveal:

dhcpcd.service          active
wpa_supplicant.service  active

NetworkManager          inactive
systemd-networkd        inactive or masked

At that point, route-priority configuration should be made in the configuration actually used by dhcpcd, represented here as:

<DHCPCD_CONFIG>

rather than modifying an inactive NetworkManager profile or an unused networkd file.


3. Understanding the USB Cellular Interface

Many USB cellular dongles do not appear to Linux as a traditional modem.

Instead, they behave like a miniature router connected over USB.

For example:

USB device
   ↓
RNDIS / CDC Ethernet
   ↓
Linux sees an Ethernet-like interface
   ↓
DHCP
   ↓
private address + gateway

Linux may detect it with a driver such as:

rndis_host

and assign a name similar to:

enx<MAC-derived-name>

The USB device itself may internally provide:

Host address:    <USB_PRIVATE_IP>
Gateway:         <USB_PRIVATE_GATEWAY>
DNS:             <USB_PRIVATE_GATEWAY>

From Linux’s perspective, this is simply another routed Ethernet interface.

This also explains why tools such as ModemManager may report:

No modems were found

even though cellular Internet connectivity works perfectly.

The modem functionality is hidden behind the USB device’s own embedded router.


4. The Problem with MAC-Derived enx... Names

A Linux USB Ethernet interface may initially appear as:

enx<DEVICE_MAC>

For ordinary USB Ethernet adapters, this is often stable.

Some cellular or tethering devices, however, can present a different MAC address after:

  • reboot,
  • USB power cycle,
  • firmware reset,
  • network restart,
  • device replacement.

That creates a serious configuration problem.

Suppose the configuration contains:

interface enx123456789abc
metric 300

and after the next USB reset Linux creates:

enxabcdef123456

The route rule no longer applies.

The physical device is still working, but the operating system sees it under a different name.


5. Why a Wildcard Is Not a Reliable Solution

It may be tempting to write:

interface enx*
metric 300

However, a configuration syntax that accepts an interface name does not necessarily treat shell-style wildcards as expected.

A useful diagnostic clue is the resulting route metric.

Suppose the intended configuration is:

metric 200

but the actual route becomes:

metric 1003

That strongly suggests that the custom rule did not match, and dhcpcd assigned its normal automatic metric instead.

So a wildcard such as:

enx*

should not be relied upon unless the exact behavior of the installed dhcpcd version has been verified.

A cleaner design is to solve the naming problem itself.


6. Creating a Stable Logical Interface Name

The better abstraction is:

physical USB device
        ↓
udev / systemd link rule
        ↓
cell0
        ↓
dhcpcd

Instead of teaching routing rules about every possible enx... name, the USB cellular interface is renamed to a stable logical name:

cell0

The physical name becomes irrelevant.


7. Identifying the USB Network Device

Before creating the naming rule, the interface should be inspected.

Useful properties include:

ID_NET_DRIVER=<USB_NETWORK_DRIVER>
ID_VENDOR_ID=<USB_VENDOR_ID>
ID_MODEL_ID=<USB_MODEL_ID>
ID_MODEL=<DEVICE_MODEL>
ID_PATH=<USB_TOPOLOGY_PATH>

For example:

ID_NET_DRIVER=rndis_host
ID_VENDOR_ID=<VENDOR_ID>
ID_MODEL_ID=<PRODUCT_ID>
ID_MODEL=<CELLULAR_DEVICE_MODEL>
ID_PATH=<USB_PATH>

These properties are generally much more useful for identifying the device than its MAC-derived interface name.


8. Creating the .link Rule

A custom systemd.link rule can rename the interface before higher-level networking configures it.

Represent the custom file as:

<SYSTEMD_LINK_RULE>

A port-specific rule could look like:

[Match]
Driver=rndis_host
Path=<USB_PATH_PATTERN>

[Link]
NamePolicy=
Name=cell0

The important result is:

changing enx... name
        ↓
always becomes
        ↓
cell0

A dry test should show something equivalent to:

Config file <SYSTEMD_LINK_RULE> is applied
Policies didn't yield a name, using specified Name=cell0
ID_NET_NAME=cell0

That confirms that the rename rule matches before a reboot is performed.


9. USB-Port-Dependent vs Hardware-Dependent Matching

There are two main ways to identify the device.

Port-based matching

Example:

[Match]
Driver=rndis_host
Path=<USB_PORT_PATH>

Advantages:

  • highly deterministic,
  • simple,
  • avoids accidentally matching another similar device.

Disadvantage:

  • moving the dongle to another physical USB port changes the path.

This means the rule may stop matching.


Hardware-based matching

A more portable rule can match stable USB properties instead of the physical port.

Conceptually:

[Match]
Driver=<USB_NETWORK_DRIVER>
Property=ID_VENDOR_ID=<VENDOR_ID> ID_MODEL_ID=<PRODUCT_ID>

[Link]
NamePolicy=
Name=cell0

This design allows the device to be moved between USB ports while preserving:

cell0

It is usually preferable when USB-port flexibility matters.


10. Route Priority Configuration

Once the cellular device is permanently represented as cell0, the routing configuration becomes very simple.

The relevant portion of <DHCPCD_CONFIG> can be:

# Wired Ethernet - highest priority
interface eth0
metric 100

# Wi-Fi - second priority
interface wlan0
metric 200

# USB cellular - final fallback
interface cell0
metric 300

This creates a clean abstraction:

physical modem identity
      handled by .link rule

routing priority
      handled by dhcpcd

The two problems are separated.

That is much cleaner than mixing USB hardware names directly into route configuration.


11. Safe Configuration Backups

Before modifying network configuration on a remotely managed server, a copy should be made.

A simple timestamp-only convention avoids ambiguity:

<DHCPCD_CONFIG>.<YYYYMMDD-HHMMSS>

Example:

<DHCPCD_CONFIG>.20260905-135542

Likewise:

<SYSTEMD_LINK_RULE>.20260905-140102

No extra words such as:

.bak
.backup
.old

are necessary.

This provides chronological versioning while keeping filenames concise.


12. Reboot Verification

After applying the stable interface name and route metrics, a reboot provides the cleanest full test.

Immediately after boot, suppose:

eth0   disconnected
wlan0  disconnected
cell0  connected

The routing table should contain only:

default via <CELLULAR_GATEWAY> dev cell0 metric 300

An actual route lookup should also show:

<TEST_PUBLIC_IP> via <CELLULAR_GATEWAY> dev cell0

This confirms that the cellular link independently provides management connectivity.


13. Bringing Wi-Fi Online

If Wi-Fi is then connected manually, the routing table should change automatically:

default via <WIFI_GATEWAY>     dev wlan0 metric 200
default via <CELLULAR_GATEWAY> dev cell0 metric 300

A route lookup should now select:

dev wlan0

because:

200 < 300

No manual route deletion is required.

dhcpcd manages both routes simultaneously and Linux selects the preferred one.


14. Why Manual Wi-Fi Can Be the Better Design

For a portable Raspberry Pi, automatically reconnecting Wi-Fi is not always desirable.

A system that moves between:

  • home,
  • office,
  • hotel,
  • temporary worksite,
  • remote installation,
  • customer location,

often encounters networks it has never seen before.

In such an environment, a better operational model is:

Boot
 ↓
USB cellular comes online automatically
 ↓
SSH remains available
 ↓
operator connects remotely
 ↓
local Wi-Fi is selected manually
 ↓
wlan0 appears with metric 200
 ↓
traffic automatically moves from cell0 to wlan0

This avoids depending on a previously stored Wi-Fi profile for remote access.

The cellular connection acts as a management lifeline.


15. Final Runtime Behavior

With all three interfaces configured, the intended behavior is:

Only cellular available

cell0 metric 300

Result:

cell0 is used

Wi-Fi and cellular available

wlan0 metric 200
cell0 metric 300

Result:

wlan0 is used
cell0 remains available as fallback

Ethernet, Wi-Fi, and cellular available

eth0  metric 100
wlan0 metric 200
cell0 metric 300

Result:

eth0 is used
wlan0 remains secondary
cell0 remains final fallback

The complete hierarchy becomes:

             ┌───────────────┐
             │    eth0       │
             │  metric 100   │
             └───────┬───────┘
                     │
                 preferred
                     │
             ┌───────▼───────┐
             │    wlan0      │
             │  metric 200   │
             └───────┬───────┘
                     │
                  fallback
                     │
             ┌───────▼───────┐
             │    cell0      │
             │  metric 300   │
             └───────────────┘

16. Replacing the USB Cellular Device Later

The abstraction around cell0 makes future hardware replacement much easier.

The rest of the system continues to reference:

cell0

Therefore this does not need to change:

interface cell0
metric 300

Only the rule that maps physical hardware to cell0 may need modification.

The replacement workflow is:

new USB device
    ↓
inspect driver / vendor / product / path
    ↓
update <SYSTEMD_LINK_RULE>
    ↓
new device is named cell0
    ↓
existing dhcpcd configuration continues working

The routing configuration does not care whether the underlying device is:

old modem
new modem
phone tethering adapter
USB router

as long as the new network device is mapped to:

cell0

17. Different USB Networking Technologies

A replacement device may not use RNDIS.

Possible drivers include:

rndis_host
cdc_ether
cdc_ncm
cdc_mbim
qmi_wwan

The first three often expose an Ethernet-like interface directly.

Devices using:

cdc_mbim
qmi_wwan

may require a different architecture involving tools such as:

ModemManager
MBIM
QMI

In that case, merely renaming the interface may not be sufficient.

However, for ordinary USB tethering devices that expose DHCP-based Ethernet networking, the cell0 abstraction remains a very effective design.


18. DNS Behavior

Each uplink may advertise its own DNS server.

For example:

Ethernet:
DNS = <ETHERNET_DNS>

Wi-Fi:
DNS = <WIFI_DNS>

cell0:
DNS = <CELLULAR_DNS>

systemd-resolved may retain DNS information for more than one active link.

This is related to, but separate from, IPv4 route priority.

A routing table may clearly prefer:

wlan0 metric 200

over:

cell0 metric 300

while systemd-resolved still knows DNS servers for both interfaces.

For most ordinary configurations this is acceptable, but DNS failover should be evaluated separately if strict per-interface DNS routing is required.


19. Route Metrics Are Not Full Link Health Monitoring

Metric-based failover works very well when an interface or default route disappears.

For example:

Wi-Fi disconnects
    ↓
wlan0 route disappears
    ↓
cell0 route becomes preferred

However, there is an important limitation.

Suppose Wi-Fi remains associated and retains:

IP address
gateway
default route

but the upstream Internet connection behind the Wi-Fi router is broken.

Linux still sees:

wlan0 metric 200

and may continue trying to use it.

Route metrics alone do not continuously verify Internet reachability.

True upstream health checking requires additional logic such as:

periodic connectivity probe
        ↓
route policy adjustment
        ↓
automatic uplink failover

For many portable-server deployments, ordinary interface-level failover is sufficient. More advanced health-based routing should only be added when genuinely required.


20. Why This Architecture Works Well

The final design separates four responsibilities cleanly:

wpa_supplicant
    └─ Wi-Fi authentication

dhcpcd
    └─ DHCP + route metrics

systemd .link / udev
    └─ stable interface naming

Linux routing table
    └─ uplink selection

This separation produces several benefits.

Stable configuration

The routing layer no longer depends on changing USB MAC-derived names.

Predictable failover

Priority is explicit:

100 → Ethernet
200 → Wi-Fi
300 → Cellular

Remote recoverability

The USB cellular connection can provide immediate SSH access after boot even when no known Wi-Fi network exists.

Portable deployment

Wi-Fi can be selected manually according to the current location.

Easy hardware replacement

Only the physical-to-logical naming rule must be adapted when the USB tethering hardware changes.


Conclusion

A reliable multi-uplink Raspberry Pi does not require a complicated routing framework when the requirements are straightforward.

The key is to separate physical hardware identity from logical networking policy.

The resulting architecture is:

Physical hardware
      ↓
stable logical names
      ↓
dhcpcd route metrics
      ↓
Linux route selection

with the final policy:

Ethernet  metric 100
Wi-Fi     metric 200
Cellular  metric 300

and the USB cellular device permanently represented as:

cell0

This turns a potentially fragile combination of wired networking, changing Wi-Fi environments, and unstable USB interface names into a simple and maintainable failover system suitable for a portable Linux server.

Leave a Reply

Your email address will not be published. Required fields are marked *