mcpField
C
cloud-init-coder

cloud-init-coder

github.com/majesticlabs-devCloud & DevOps5075 installsUpdated 7w ago
Repository

About cloud-init-coder

cloud-init-coder is a skill reference for writing cloud-init configurations to initialize cloud instances. It covers user and package setup, security, services, files, and infrastructure templates.

Key features

  1. 01Create users and configure SSH
  2. 02Install packages and repositories
  3. 03Configure Docker and systemd services
  4. 04Write files and set up firewalls
  5. 05Use Terraform/OpenTofu templates
  6. 06Debug cloud-init runs

Use cases

  1. 01Prepare cloud instances on first boot
  2. 02Harden SSH access and configure users
  3. 03Deploy Docker, web, or system services
  4. 04Troubleshoot cloud-init configuration issues

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill cloud-init-coder

After installing, type/cloud-init-coderin Claude Code to confirm it loaded.

Details

From the project README

Cloud-Init Coder

Overview

Cloud-init is the industry standard for cross-platform cloud instance initialization. It runs on first boot to configure users, packages, files, and services before the instance becomes available.

Core Format

Cloud-init configs start with #cloud-config:

#cloud-config
package_update: true
packages:
  - nginx
  - docker.io

User Management

Create Deploy User
#cloud-config
users:
  - name: deploy
    groups: docker, sudo
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... [email protected]
Multiple Users
#cloud-config
users:
  - default  # Keep cloud provider's default user
  - name: deploy
    groups: docker
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... key1
  - name: monitoring
    groups: adm
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... monitoring-key

Package Installation

Basic Packages
#cloud-config
package_update: true
package_upgrade: true
packages:
  - docker.io
  - docker-compose-plugin
  - nginx
  - certbot
  - python3-certbot-nginx
  - fail2ban
  - ufw
From Custom Repositories
#cloud-config
apt:
  sources:
    docker:
      source: "deb [arch=amd64] https://download.docker.com/linux/ubuntu $RELEASE stable"
      keyid: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88

packages:
  - docker-ce
  - docker-ce-cli
  - containerd.io

SSH Hardening

Declarative SSH Lockdown

Prefer declarative ssh_pwauth: false over runcmd sed commands:

#cloud-config
ssh_pwauth: false  # Disable password auth at cloud-init level

runcmd:
  # Additional hardening via sshd_config
  - sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
  - systemctl restart sshd
Full SSH Hardening
#cloud-config
ssh_pwauth: false  # Declarative - cleaner than sed

runcmd:
  # Disable root login (or use prohibit-password for key-only root)
  - sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
  - sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config

  # Disable password authentication (backup for ssh_pwauth)
  - sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config

  # Increase keepalive for stable connections
  - sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 60/' /etc/ssh/sshd_config
  - sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 10/' /etc/ssh/sshd_config

  # Restart SSH
  - systemctl restart sshd

Docker Setup

Docker with Compose
#cloud-config
package_update: true
packages:
  - docker.io
  - docker-compose-plugin

groups:
  - docker

users:
  - name: deploy
    groups: docker
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAA...

runcmd:
  - systemctl enable --now docker
  - usermod -aG docker deploy
Docker with Custom Daemon Config
#cloud-config
write_files:
  - path: /etc/docker/daemon.json
    content: |
      {
        "log-driver": "json-file",
        "log-opts": {
          "max-size": "10m",
          "max-file": "3"
        },
        "storage-driver": "overlay2"
      }

runcmd:
  - systemctl enable --now docker

File Creation

Write Configuration Files
#cloud-config
write_files:
  - path: /etc/nginx/sites-available/app
    content: |
      server {
          listen 80;
          server_name example.com;
          location / {
              proxy_pass http://127.0.0.1:3000;
              proxy_set_header Host $host;
              proxy_set_header X-Real-IP $remote_addr;
          }
      }
    owner: root:root
    permissions: '0644'

  - path: /opt/app/.env
    content: |
      RAILS_ENV=production
      PORT=3000
    owner: deploy:deploy
    permissions: '0600'
Download Files
#cloud-config
runcmd:
  - curl -fsSL https://example.com/setup.sh -o /opt/setup.sh
  - chmod +x /opt/setup.sh
  - /opt/setup.sh

Service Configuration

Enable and Start Services
#cloud-config
runcmd:
  - systemctl enable --now docker
  - systemctl enable --now nginx
  - systemctl enable --now fail2ban
Systemd Service Creation
#cloud-config
write_files:
  - path: /etc/systemd/system/myapp.service
    content: |
      [Unit]
      Description=My Application
      After=network.target docker.service
      Requires=docker.service

      [Service]
      Type=simple
      User=deploy
      WorkingDirectory=/opt/app
      ExecStart=/usr/bin/docker compose up
      ExecStop=/usr/bin/docker compose down
      Restart=always
      RestartSec=10

      [Install]
      WantedBy=multi-user.target

runcmd:
  - systemctl daemon-reload
  - systemctl enable --now myapp

Firewall Configuration

UFW Setup
#cloud-config
packages:
  - ufw

runcmd:
  - ufw default deny incoming
  - ufw default allow outgoing
  - ufw allow ssh
  - ufw allow http
  - ufw allow https
  - ufw --force enable

Terraform/OpenTofu Integration

Inline User Data
resource "digitalocean_droplet" "app" {
  name   = "app-server"
  image  = "ubuntu-22-04-x64"
  size   = "s-1vcpu-1gb"
  region = "nyc1"

  user_data = <<-EOT
    #cloud-config
    package_update: true
    packages:
      - docker.io
      - docker-compose-plugin
    users:
      - name: deploy
        groups: docker
        sudo: ALL=(ALL) NOPASSWD:ALL
        shell: /bin/bash
        ssh_authorized_keys:
          - ${var.deploy_ssh_key}
    runcmd:
      - systemctl enable --now docker
  EOT
}
Template File
# templates/cloud-init.yaml
#cloud-config
package_update: true
packages:
  - docker.io
users:
  - name: ${username}
    groups: docker
    ssh_authorized_keys:
      - ${ssh_key}

# main.tf
resource "digitalocean_droplet" "app" {
  user_data = templatefile("${path.module}/templates/cloud-init.yaml", {
    username = var.deploy_user
    ssh_key  = var.deploy_ssh_key
  })
}

Complete Production Example

#cloud-config
package_update: true
package_upgrade: true

packages:
  - docker.io
  - docker-compose-plugin
  - fail2ban
  - ufw
  - unattended-upgrades

groups:
  - docker

users:
  - name: deploy
    groups: docker, sudo
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-ed25519 AAAA... deploy-key

write_files:
  - path: /etc/docker/daemon.json
    content: |
      {
        "log-driver": "json-file",
        "log-opts": { "max-size": "10m", "max-file": "3" }
      }

  - path: /etc/fail2ban/jail.local
    content: |
      [sshd]
      enabled = true
      port = ssh
      filter = sshd
      maxretry = 3
      bantime = 3600

runcmd:
  # Docker
  - systemctl enable --now docker

  # SSH hardening
  - sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
  - sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
  - sed -i 's/^#\?ClientAliveInterval.*/ClientAliveInterval 60/' /etc/ssh/sshd_config
  - sed -i 's/^#\?ClientAliveCountMax.*/ClientAliveCountMax 10/' /etc/ssh/sshd_config
  - systemctl restart sshd

  # Firewall
  - ufw default deny incoming
  - ufw default allow outgoing
  - ufw allow ssh
  - ufw allow http
  - ufw allow https
  - ufw --force enable

  # Fail2ban
  - systemctl enable --now fail2ban

  # Auto-updates
  - systemctl enable --now unattended-upgrades

final_message: "Cloud-init completed after $UPTIME seconds"

Server Tuning

Performance and Cleanup
#cloud-config
runcmd:
  # Reduce swap usage (better for databases/apps with their own memory management)
  - |
    if ! grep -q "vm.swappiness=10" /etc/sysctl.conf; then
      echo "vm.swappiness=10" >> /etc/sysctl.conf
      sysctl -p
    fi

  # Set timezone
  - timedatectl set-timezone UTC  # Or: Europe/Berlin, America/New_York

  # Cleanup
  - apt-get autoremove -y
  - apt-get clean
Swappiness Values
ValueBehavior
0Only swap to avoid OOM
10Minimal swapping (recommended for apps)
60Default Ubuntu
100Aggressive swapping

Debugging

Check Cloud-Init Status
# View cloud-init status
cloud-init status

# View cloud-init logs
cat /var/log/cloud-init.log
cat /var/log/cloud-init-output.log

# Re-run cloud-init (for testing)
sudo cloud-init clean
sudo cloud-init init
Common Issues
IssueCauseFix
YAML parse errorIndentation wrongUse 2-space indent, validate YAML
User not createdMissing users: keyEnsure users: is at root level
Packages not installedpackage_update: falseSet package_update: true
SSH key rejectedWrong key formatUse full public key string
Service not startingOrder dependencyUse After= in systemd unit

Comments

Markdown supported