canonical / canonical/cloud-init
NoCloud datasource loaded from ds=nocloud;s=file:///... does not apply supplied network-config
- 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
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