Skip to content

cloud-init user-data for a no-Ansible single-VM node bootstrap #72

Description

@thiras

Context

The repo now has three deploy paths: Ansible, Docker Compose and Helm (#69). All three assume the operator already has something set up: a control machine with Ansible, a Docker host, or a cluster.

The lowest-friction path for many operators is paste a user-data file into a VPS provider's "create server" form. Almost every provider accepts cloud-init there (Hetzner, DigitalOcean, OVHcloud, Vultr, AWS, Scaleway, …). This was listed as optional in the #69 review and deferred.

This also fits #48 (Terraform for DigitalOcean): the same user-data can be the Droplet's user_data, so Terraform provisions and cloud-init bootstraps without a separate hand-off.

Proposal

Add cloud-init/ with a documented user-data.yaml template that brings a fresh Debian 12/13 or Ubuntu 24.04/26.04 host (x86_64 or aarch64) to "node installed, waiting for its secret".

Recommended approach: ansible-pull of this repo's site.yml against localhost. It reuses the baseline hardening (nftables, DevSec, fail2ban, unattended-upgrades) and the decdn_node role unchanged, so there is no second copy of the node's configuration. The alternative, cloud-init installing Docker and running compose/, is simpler but skips host hardening. Decide in the PR.

Constraints (AGENTS.md hard rules)

  • No secrets in user-data. User-data is readable from the instance metadata service by any local process, and providers keep it in their console and API. The RPC URL (which may embed an API key) must not go in it.
    • The bootstrap should stop at the role's existing "secret env file missing" gate, or leave the unit disabled.
    • The operator then provisions 0600 /etc/decdn/decdn.env over SSH, the path the role already prefers.
    • The keystore should be generated on the host (decdn_node_generate_keystore: true), never passed in.
  • Pinned and verified.
    • Pin this repo by tag or commit and the collections by version.
    • Install the node from the GPG-verified release tarball (release mode).
    • No curl | bash of unpinned content.
  • Localhost-only default and one public hole (udp/4433): unchanged, because the roles enforce them.
  • Lockout safety. The SSH key must come from the provider's key injection or an explicit baseline_sudo_users entry, since baseline disables root and password login.

Blocked on

An upstream decdn/decdn release. manual install mode copies binaries from a control machine, which a cloud-init bootstrap doesn't have. Until upstream tags a release, this path can only be prototyped against a locally hosted tarball mirror (decdn_node_release_base).

Acceptance criteria

  • cloud-init/user-data.yaml passes cloud-init schema --config-file and pre-commit, and contains no secret.
  • Documented end to end in cloud-init/README.md:
    • create the VM with the user-data;
    • SSH in and write decdn.env;
    • start or re-run the node;
    • decdn_chain setup (docs/lifecycle.md).
  • A CI check boots the user-data in a container or VM (e.g. an LXD/cloud-image or molecule scenario with cloud-init) at least as far as the secret gate. At minimum, the rendered ansible-pull invocation is syntax-checked.
  • Added to the deploy-path chooser in docs/requirements.md and the root README.
  • Cross-referenced from Terraform integration for DigitalOcean VPS and Block Storage #48 as the Droplet user_data option.

Open questions

  • ansible-pull (hardening included) or Compose (no Ansible on the host)?
  • How the operator is told the host is ready for its secret: a MOTD line, cloud-init status, or a marker file.
  • Whether to take the region (decdn_region) as a templated user-data variable, since it isn't a secret.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Fields

Priority

None yet

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions