Imagine launching a new server, and within minutes it’s fully configured and ready to work — without a single manual action. Sounds unbelievable? But it’s a reality thanks to the cloud-init tool. This technology allows you to automatically configure a server right after launch by passing all the required parameters through a special configuration file. As a result, the system itself installs the necessary packages, sets up the web server, updates the OS, and even starts your app — all without administrator involvement.

In the RX-NAME service, cloud-init capabilities are available right from the control panel. You can use them in just a few clicks, making server deployment as fast, reliable, and convenient as possible for developers and businesses.

User-data usage examples

Many IT resources provide examples of how to pass configuration data (user-data) during virtual machine launch for automatic server setup. Such cases often use scripts that install and configure the NGINX web server, install Node.js, and launch web applications. This approach allows you to define all security settings, reverse proxy configuration, and auto-start parameters for the application from the start. As a result, time-to-market is significantly reduced.

What is cloud-init and why is it important?

Cloud-init is a universal tool for automatic initialization of cloud servers. It enables a wide range of tasks:

  • Package installation and updates. With cloud-init, the server immediately gets all necessary tools (e.g., curl, git, apt-transport-https) to prepare the system for further operation.
  • NGINX web server setup. Configuration files are automatically created to ensure secure operation of NGINX and proper proxying of requests to the application.
  • Installing Node.js and dependencies. The script checks for the required versions of Node.js and npm, updates them if needed, and installs project dependencies.
  • Automatic application launch. Using systemd, you can add a unit file to launch your app as a service. This ensures it runs continuously and restarts automatically if it crashes.

Thanks to these features, cloud-init eliminates human error, reduces misconfiguration risks, and significantly speeds up server deployment. For more details on the functions and advantages of cloud-init, visit our blog — we’ve prepared a separate article on this topic.

Cloud-init script example for deploying a Svelte app

Let’s take a real example: the configuration file below performs automatic deployment and setup of the web server, Node.js, and the Svelte application itself. Here’s what happens, step by step:

  1. Installing base packages. The packages section lists utilities (curl, git, apt-transport-https) that need to be installed at launch. This ensures the server has all tools to fetch keys, add repositories, and clone projects.
  2. Creating configuration files. With the write_files directive, the script creates several config files:
    • Security headers snippet for NGINX: adds a set of rules to protect the server from XSS, clickjacking, etc.
    • Default site config: sets a basic page for testing NGINX without proxying.
    • Reverse proxy config for the Svelte app: redirects requests to the app running on port 8080.
    • Test HTML page: confirms proper operation of the web server.
    • systemd unit file: defines a service for running and restarting the app automatically.
  3. Executing commands (runcmd). This block lists the commands executed to configure the server step by step:
    • Verifying presence of NGINX and Node.js: checks for existing installations and logs version info.
    • Adding repository and installing NGINX: connects the official repo, installs the latest version, and verifies its operation.
    • Installing Node.js v20: if missing or outdated, installs the latest version with npm.
    • Cloning and building the Svelte app: downloads the example project from GitHub, installs dependencies, and assigns directory permissions.
    • Starting via systemd: launches the service, checks availability on port 8080.
    • Enabling Svelte app proxy config in NGINX: enables svelte_app_proxy, disables the default config, and restarts the server after checking syntax.
#cloud-config
packages:
- curl
- git
- apt-transport-https

write_files:
# Security headers snippet for nginx
- path: /etc/nginx/snippets/security-headers.conf
  permissions: '0644'
  content: |
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header Cache-Control "no-cache, no-store, max-age=0, must-revalidate" always;
    add_header Pragma "no-cache" always;

# Default nginx site configuration (without proxying)
- path: /etc/nginx/sites-available/default
  permissions: '0644'
  content: |
    server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name localhost;
        root /var/www/html;
        index index.html index.htm;
        location / {
            try_files $uri $uri/ =404;
            include /etc/nginx/snippets/security-headers.conf;
        }
    }

# Nginx configuration for proxying requests to the Svelte app
- path: /etc/nginx/sites-available/svelte_app_proxy
  permissions: '0644'
  content: |
    server {
        listen 80;
        server_name localhost;
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
            include /etc/nginx/snippets/security-headers.conf;
        }
    }

# Simple default HTML page for nginx test
- path: /var/www/html/index.html
  permissions: '0644'
  content: |
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>NGINX Test</title>
      </head>
      <body>
        <h1>Welcome to NGINX!</h1>
        <p>NGINX is running correctly!</p>
      </body>
    </html>

# systemd unit file to run the Svelte app
- path: /etc/systemd/system/svelte-app.service
  permissions: '0644'
  content: |
    [Unit]
    Description=Svelte Application Service
    After=network.target

    [Service]
    WorkingDirectory=/var/www/svelte-app
    ExecStart=/usr/bin/npx sirv public --no-clear --dev --host 127.0.0.1 --port 8080
    Restart=always
    User=www-data
    Environment=NODE_ENV=production

    [Install]
    WantedBy=multi-user.target

runcmd:
- |
    LOG_FILE="/var/log/cloud-init-custom.log"
    echo "[$(date)] *** Starting cloud-init script ***" >> ${LOG_FILE}

    # Check for nginx and Node.js/npm
    if command -v nginx >/dev/null 2>&1; then
        INSTALLED_NGINX=$(nginx -v 2>&1)
        echo "[$(date)] NGINX is already installed: ${INSTALLED_NGINX}" >> ${LOG_FILE}
    else
        echo "[$(date)] NGINX not found" >> ${LOG_FILE}
    fi

    if command -v node >/dev/null 2>&1; then
        NODE_VER=$(node -v)
        echo "[$(date)] Node.js is already installed: ${NODE_VER}" >> ${LOG_FILE}
    else
        echo "[$(date)] Node.js not found" >> ${LOG_FILE}
    fi

    if command -v npm >/dev/null 2>&1; then
        NPM_VER=$(npm -v)
        echo "[$(date)] npm is already installed: ${NPM_VER}" >> ${LOG_FILE}
    else
        echo "[$(date)] npm not found" >> ${LOG_FILE}
    fi

    # Add official nginx repository and install the latest version
    echo "deb http://nginx.org/packages/ubuntu/ ${UBUNTU_CODENAME:-jammy} nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
    echo "deb-src http://nginx.org/packages/ubuntu/ ${UBUNTU_CODENAME:-jammy} nginx" | sudo tee -a /etc/apt/sources.list.d/nginx.list
    curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
    sudo apt-get update >> ${LOG_FILE} 2>&1
    sudo apt-get install -y nginx >> ${LOG_FILE} 2>&1
    echo "[$(date)] NGINX installed: $(nginx -v 2>&1)" >> ${LOG_FILE}

    # Install Node.js v20
    if command -v node >/dev/null 2>&1; then
        CURRENT_NODE=$(node -v | sed 's/v//')
        if [ "$(printf '%s\n' "20.0.0" "$CURRENT_NODE" | sort -V | head -n1)" = "20.0.0" ]; then
            echo "[$(date)] Node.js is up to date ($CURRENT_NODE)" >> ${LOG_FILE}
        else
            curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - >> ${LOG_FILE} 2>&1
            sudo apt-get install -y nodejs >> ${LOG_FILE} 2>&1
        fi
    else
        curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - >> ${LOG_FILE} 2>&1
        sudo apt-get install -y nodejs >> ${LOG_FILE} 2>&1
    fi

    # Check npm installation
    if ! command -v npm >/dev/null 2>&1; then
        echo "[$(date)] Error: npm is not installed" >> ${LOG_FILE}
        exit 1
    fi

    # Test nginx default config
    sudo systemctl restart nginx
    if ! sudo systemctl is-active --quiet nginx; then
        echo "[$(date)] Failed to start nginx" >> ${LOG_FILE}
        exit 1
    fi

    HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1)
    if [ "$HTTP_CODE" -ne 200 ]; then
        echo "[$(date)] Default page did not return HTTP 200" >> ${LOG_FILE}
        exit 1
    fi

    # Clone and set up the Svelte app
    SVELTE_DIR="/var/www/svelte-app"
    if [ ! -d "${SVELTE_DIR}" ]; then
        sudo git clone https://github.com/sveltejs/template ${SVELTE_DIR} >> ${LOG_FILE} 2>&1
        sudo chown -R www-data:www-data ${SVELTE_DIR}
    fi

    cd ${SVELTE_DIR}
    if [ -f package.json ]; then
        npm install >> ${LOG_FILE} 2>&1
    else
        echo "[$(date)] package.json not found" >> ${LOG_FILE}
        exit 1
    fi

    # Start Svelte with systemd
    sudo systemctl daemon-reload
    sudo systemctl enable svelte-app.service >> ${LOG_FILE} 2>&1
    sudo systemctl start svelte-app.service
    sleep 5

    if ! sudo systemctl is-active --quiet svelte-app.service; then
        echo "[$(date)] Failed to start Svelte app" >> ${LOG_FILE}
        exit 1
    fi

    SVELTE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:8080)
    if [ "$SVELTE_CODE" -ne 200 ]; then
        echo "[$(date)] Svelte app did not return HTTP 200" >> ${LOG_FILE}
        exit 1
    fi

    # Enable nginx proxy config for Svelte
    sudo mkdir -p /etc/nginx/sites-enabled
    sudo ln -sf /etc/nginx/sites-available/svelte_app_proxy /etc/nginx/sites-enabled/svelte_app_proxy
    sudo rm -f /etc/nginx/sites-enabled/default

    if ! grep -q "include /etc/nginx/sites-enabled/*;" /etc/nginx/nginx.conf; then
        sudo sed -i '/include \/etc\/nginx\/conf.d\/\*.conf;/a include /etc/nginx/sites-enabled/*;' /etc/nginx/nginx.conf
    fi

    sudo systemctl restart nginx

    PROXY_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1)
    if [ "$PROXY_CODE" -ne 200 ]; then
        echo "[$(date)] Proxy via nginx failed" >> ${LOG_FILE}
        exit 1
    fi

    echo "[$(date)] *** Svelte app setup and configuration completed successfully ***" >> ${LOG_FILE}

final_message: "Cloud-init completed. Check the log at /var/log/cloud-init-custom.log for details."

Svelte vs Nuxt vs Next: Framework comparison

When choosing a frontend framework, developers often consider Svelte, Nuxt, or Next. Each has its strengths and limitations.

Svelte

Pros:

  • Compiles into pure JavaScript without a virtual DOM, making apps extremely lightweight and fast.
  • Minimal code and high readability simplify development and maintenance.
  • Intuitive syntax and low learning curve.

Cons:

  • Smaller ecosystem and fewer ready-made solutions compared to major frameworks.
  • Fewer learning resources and examples for complex tasks compared to Nuxt and Next.

Nuxt

Pros:

  • Built on Vue.js, making it appealing to those familiar with the Vue ecosystem.
  • Has built-in server-side rendering (SSR) and static site generation features.
  • Large, active community and well-developed plugin/module ecosystem.

Cons:

  • More complex configuration and optimization than Svelte.
  • Can require more resources for development and maintenance.

Next

Pros:

  • Built on React, ensuring broad support and many ready-to-use components.
  • Excellent support for SSR and static page generation.
  • Mature ecosystem and proven usage in large projects.

Cons:

  • Might be too “heavy” for small projects where simplicity and development speed are more critical than scalability.
  • More complex setup compared to Svelte.

Conclusions

Automated server configuration with cloud-init significantly reduces infrastructure deployment and setup time. It enables not only package installation and NGINX setup, but also the deployment of a modern Svelte-based application.

Compared to Nuxt and Next, Svelte stands out for its simplicity, minimal code output, and high performance. However, for larger projects, Nuxt or Next is often preferred due to their richer ecosystems and additional capabilities.

Thanks to the integration of cloud-init in the RX-NAME user panel, you get a powerful automation tool to help you quickly and securely launch your projects. Try this approach and see for yourself how it simplifies the development and deployment process. For more details about cloud-init and other automation solutions, check out our blog.