VPS Server Provisioning: Automating Setup with Ansible, Cloud-Init, and Shell Scripts

Manually configuring a fresh VPS every time you spin one up is tedious and error-prone. Whether you are deploying a single application server or a cluster of nodes, automated provisioning saves hours and eliminates configuration drift. This guide walks through three widely used approaches: cloud-init for first-boot automation, Ansible for idempotent configuration management, and shell scripts for lightweight repeatable setup.

Why Automate VPS Provisioning?

  • Repeatability — Every server starts from the same known state, eliminating the “it works on my machine” problem.
  • Speed — Full server setup drops from 45 minutes of manual work to under 5 minutes.
  • Auditability — Your provisioning code serves as living documentation of your server configuration.
  • Scalability — Adding new nodes becomes a single command instead of a multi-hour process.

See the best VPS options for your provisioning workflow on our comparison table.

Option 1: Cloud-Init — First-Boot Automation

Most modern VPS providers support cloud-init, the cross-distribution standard for early boot configuration. You supply a YAML file (user-data) when creating the instance, and cloud-init runs it automatically on first boot.

Example: Cloud-Init User-Data for a LEMP Stack

#cloud-config
package_update: true
package_upgrade: true

packages:
  - nginx
  - mariadb-server
  - php-fpm
  - php-mysql
  - ufw

runcmd:
  - systemctl enable nginx
  - systemctl start nginx
  - systemctl enable mariadb
  - systemctl start mariadb
  - ufw allow 22/tcp
  - ufw allow 80/tcp
  - ufw allow 443/tcp
  - ufw --force enable

Cloud-init scripts run once at first boot, making them ideal for base OS configuration, package installation, and initial security hardening. For ongoing configuration changes, use a configuration management tool.

Option 2: Ansible — Idempotent Configuration Management

Ansible is agentless (uses SSH), YAML-driven, and idempotent — running the same playbook multiple times produces the same result. It is the most popular choice for VPS provisioning among system administrators.

Minimal Ansible Inventory File

[vps_servers]
web1 ansible_host=203.0.113.10 ansible_user=deploy
web2 ansible_host=203.0.113.11 ansible_user=deploy

Example Playbook: Secure SSH and Install Nginx

---
- name: Initial VPS hardening and web server setup
  hosts: vps_servers
  become: yes
  tasks:
    - name: Disable root SSH login
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: "^PermitRootLogin"
        line: "PermitRootLogin no"
      notify: restart sshd

    - name: Install Nginx
      apt:
        name: nginx
        state: latest

    - name: Start and enable Nginx
      systemd:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: restart sshd
      systemd:
        name: sshd
        state: restarted

Run the playbook with ansible-playbook -i inventory.ini secure-nginx.yml. Because Ansible is idempotent, you can run it weekly to enforce the desired state.

Option 3: Shell Scripts — Lightweight and Portable

For small deployments or when you prefer simplicity, a single bash script can handle the entire setup. Shell scripts are the most portable approach — they work on any provider without additional tooling.

#!/bin/bash
set -euo pipefail

# Update system
apt-get update && apt-get upgrade -y

# Install essentials
apt-get install -y nginx mariadb-server php-fpm php-mysql ufw fail2ban htop netdata

# Configure firewall
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

# Enable services
systemctl enable nginx mariadb php8.1-fpm
systemctl start nginx

Combining Approaches for the Best Results

A production-grade VPS provisioning workflow typically combines all three:

  1. Cloud-init handles base OS package installation and firewall rules at first boot.
  2. Ansible manages application configuration, users, and security policies on an ongoing basis.
  3. Shell scripts wrap one-off tasks (database import, SSL certificate generation).

Store your provisioning code in a Git repository and tag releases to match server deployments. This gives you full version control over your infrastructure.

Conclusion

Automated VPS provisioning is not optional once you manage more than a single server — it is a necessity. Start with cloud-init for first-boot tasks, adopt Ansible for ongoing configuration management, and use shell scripts for quick, one-off operations. Your future self will thank you every time a new server deploys in minutes instead of hours.

Compare providers that support cloud-init and other automation features on our comparison table.

Leave a Reply