canonical / canonical/cloud-init

NoCloud datasource loaded from ds=nocloud;s=file:///... does not apply supplied network-config

Open
#7,087 1 comment 0 reactions 0 assignees View on GitHub
bug new
Dominant language
Python
Stars
3.8k
Forks
1.1k
Avg merge
2d 23h
Merged PRs (30d)
18

Description

# Bug Report

## Summary

Cloud-init successfully reads `user-data` and `meta-data` from a NoCloud datasource specified via `ds=nocloud;s=file:///...`. However, there is no corresponding log evidence that `network-config` is read from the same seed source. Cloud-init subsequently applies fallback networking, and the resulting network configuration does not match the supplied `network-config`.

The identical seed files function correctly when provided via CIDATA USB or ISO media.

## Key Observations

The evidence collected indicates:

- `meta-data` is read from the `seedfrom` path.
- `user-data` is read from the `seedfrom` path.
- `network-config` is not observed being read from the `seedfrom` path.
- Cloud-init subsequently applies fallback networking.
- The effective network configuration does not match the supplied `network-config`.
- The identical files work when presented as CIDATA media (USB or ISO).

## Description

When cloud-init is configured to use the NoCloud datasource via kernel command line:

```text
ds=nocloud;s=file:///path/to/config/
```

cloud-init successfully discovers and applies both `user-data` and `meta-data` from the specified seed directory. However, a valid `network-config` file located in the same directory is not applied.

The seed directory contains all three expected files:

```text
user-data
meta-data
network-config
```

and cloud-init successfully consumes the `user-data` and `meta-data` content from that location.

As a comparison, the exact same set of seed files works correctly when provided through a CIDATA-labeled ISO or USB device, where `user-data`, `meta-data`, and `network-config` are all applied as expected.

As a sanity check, the `network-config` file was validated using:

```bash
cloud-init devel net-convert
```

which successfully generated the corresponding Netplan configuration, confirming the file is syntactically valid.

Based on this behavior, there appears to be an inconsistency between NoCloud seed discovery using a `file://` seedfrom path and NoCloud seed discovery using traditional CIDATA media.

## Expected Behavior

When a NoCloud datasource is configured using:

```text
ds=nocloud;s=file:///path/to/config/
```

cloud-init should discover and process all supported seed files located in the specified directory, including:

```text
user-data
meta-data
network-config
```

## Actual Behavior

```text
user-data -> processed
meta-data -> processed
network-config -> not applied
```

The same `network-config` file is successfully applied when provided via CIDATA media, indicating the issue is specific to the `seedfrom`-based NoCloud workflow rather than the network configuration itself.

## Steps to Reproduce

1. Create a NoCloud seed directory containing:

```text
user-data
meta-data
network-config
```

2. Populate `network-config` with a valid cloud-init network configuration:

```yaml
#cloud-config
network:
version: 2
ethernets:
eth0:
dhcp4: true
dhcp6: false
dhcp-identifier: mac
dhcp4-overrides:
route-metric: 100
use-dns: true
use-ntp: true
use-mtu: true
use-routes: true
eth1:
dhcp4: false
dhcp6: false
addresses:
- 192.168.0.1/25
ignore-carrier: true
wifis: {}
```

3. Validate the configuration:

```bash
cloud-init devel net-convert \
--network-data network-config \
--kind yaml \
--output-kind netplan \
--distro ubuntu \
-d /tmp/netconvert
```

4. Configure the system to use:

```text
ds=nocloud;s=file:///path/to/seed/
```

5. Boot a clean system and allow cloud-init to complete first-boot initialization.

6. Verify that `user-data` and `meta-data` are applied.

7. Verify whether the supplied `network-config` was applied.

8. Repeat the test with the identical files delivered through a CIDATA USB or ISO.

## Observed Results

With:

```text
ds=nocloud;s=file:///path/to/seed/
```

- `user-data` is applied.
- `meta-data` is applied.
- `network-config` is not applied.
- Fallback networking is applied instead.

With the same files provided via CIDATA media:

- `user-data` is applied.
- `meta-data` is applied.
- `network-config` is applied.

## Environment

**Linux Distribution:** Ubuntu Core 24

**Platform:** NoCloud (Physical and Hyper-V)

```bash
# cloud-init --version
/usr/bin/cloud-init 25.3-0ubuntu1~24.04.1

# uname -a
Linux localhost 7.0.0-31-generic #31~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Aug 10 09:38:02 UTC x86_64 x86_64 x86_64 GNU/Linux
```

## Effective Network Configuration

The supplied configuration contains:

- `dhcp6: false` on `eth0`
- static configuration for `eth1`
- DHCP overrides
- `ignore-carrier`

However, cloud-init ultimately produces:

```json
{
"ethernets": {
"eth0": {
"dhcp4": true,
"dhcp6": true,
"match": {
"macaddress": "00:15:5d:c2:d7:2c"
},
"set-name": "eth0"
}
},
"version": 2
}
```

Notable differences:

- `dhcp6: false` becomes `dhcp6: true`
- `eth1` is missing
- static addressing is missing
- DHCP overrides are missing
- `ignore-carrier` is missing

## Relevant Log Evidence

A complete `cloud-init collect-logs` archive is attached.
[cloud-init.tar.gz](https://github.com/user-attachments/files/32146653/cloud-init.tar.gz)

The attached logs show that cloud-init reads `meta-data` and `user-data` from the configured seed source (`file:///var/lib/snapd/seed/cidata/`). However, there is no corresponding log entry showing `network-config` being read from the same location.

```text
# grep -RE "(Reading.*(user-data|meta-data|network-config)|fallback)"

2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/user-data (quiet=False)
2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/meta-data (quiet=False)
2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud/network-config (quiet=False)
2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/user-data (quiet=False)
2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/meta-data (quiet=False)
2026-09-12 14:45:44,800 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/network-config (quiet=False)

2026-09-12 14:45:44,820 - util.py[DEBUG]: Reading from /var/lib/snapd/seed/cidata/meta-data (quiet=False)
2026-09-12 14:45:44,820 - util.py[DEBUG]: Reading 336 bytes from /var/lib/snapd/seed/cidata/meta-data

2026-09-12 14:45:44,821 - util.py[DEBUG]: Reading from /var/lib/snapd/seed/cidata/user-data (quiet=False)
2026-09-12 14:45:44,821 - util.py[DEBUG]: Reading 1943 bytes from /var/lib/snapd/seed/cidata/user-data

2026-09-12 14:45:44,879 - stages.py[INFO]: Applying network configuration from fallback bringup=False
```

Both `meta-data` and `user-data` are explicitly read from the seed source:

```text
Reading from /var/lib/snapd/seed/cidata/meta-data
Reading 336 bytes from /var/lib/snapd/seed/cidata/meta-data

Reading from /var/lib/snapd/seed/cidata/user-data
Reading 1943 bytes from /var/lib/snapd/seed/cidata/user-data
```

However, there is no corresponding:

```text
Reading from /var/lib/snapd/seed/cidata/network-config
Reading from /var/lib/snapd/seed/cidata/network-config
```

Even though the file exists in the seed directory and the same artifact is successfully processed when delivered via CIDATA USB or ISO media.

# Appendix: Additional Evidence and Suspected Root Cause Analysis

> [!NOTE]
> The observations in this appendix are based on local inspection of the cloud-init state and runtime artifacts collected from the affected system. This analysis was performed against the installed cloud-init instance and its serialized datasource state (`/var/lib/cloud/instance/obj.pkl`). It is not intended to represent a review of the upstream cloud-init source repository or to assert a definitive root cause.

## Additional Evidence That `network-config` Is Not Retained By The Datasource

Datasource object inspection after first boot shows that no network configuration is present on the initialized NoCloud datasource object.

```python
with open('/var/lib/cloud/instance/obj.pkl', 'rb') as f:
ds = pickle.load(f)

print(ds._network_config)
```

Output:

```text
None
```

Datasource origin:

```python
print(ds.seed)
```

Output:

```text
cmdline,file:///var/lib/snapd/seed/cidata/
```

Observation:

> At the completion of datasource initialization, `ds._network_config` is `None` despite a `network-config` file being present in the seed directory. This suggests that no network configuration was retained by the datasource from the supplied seed data.

## Source Code Review

During investigation, the NoCloud datasource implementation was reviewed to compare the standard seed discovery path with the `seedfrom=file://` path.

### Standard NoCloud Discovery Path

The standard NoCloud discovery logic includes support for `network-config`:

```python
pp2d_kwargs = {
"required": ["user-data", "meta-data"],
"optional": ["vendor-data", "network-config"],
}
```

Later, the datasource conditionally loads the network configuration:

```python
if seeded.get("network-config"):
ret["network-config"] = util.load_yaml(
seeded.get("network-config")
)
```

This code path explicitly supports and loads `network-config` when present.

### Alternate `seedfrom=file://` Path

When cloud-init processes a datasource configured via:

```text
ds=nocloud;s=file:///var/lib/snapd/seed/cidata/
```

execution appears to follow a different code path:

```python
md_seed, ud, vd, _ = util.read_seeded(
seedfrom,
timeout=None
)
```

Later:

```python
mydata["meta-data"] = ...
mydata["user-data"] = ud
mydata["vendor-data"] = vd
```

The fourth return value from `util.read_seeded()` is assigned to:

```python
_
```

and is not subsequently assigned to:

```python
mydata["network-config"]
```

## Why This Appears Relevant

The observed runtime behavior appears consistent with this implementation path:

- `meta-data` is processed successfully.
- `user-data` is processed successfully.
- `network-config` is not reflected in the effective configuration.
- `ds._network_config` is `None`.
- Cloud-init applies fallback networking.
- The resulting network configuration differs significantly from the supplied `network-config`.

## Suspected Root Cause

This source review suggests that the NoCloud `seedfrom=file://` code path may not retain or propagate the network configuration returned from `util.read_seeded()`, resulting in fallback networking being applied.

This is a hypothesis based on code review and observed runtime behavior. Additional confirmation from cloud-init maintainers would be appreciated.

Contributor guide

Open the contributing guide

Research direction

Start with the NoCloud datasource implementation and the util.read_seeded() path described in the issue. Compare the standard discovery path with the seedfrom=file:// path, focusing on the fourth return value and how network-config is retained. Done means the supplied network-config is processed for file:// seeds and fallback networking is no longer applied; add or run focused coverage if the repository provides it.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
66/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.