Client Area

Infrastructure as Code on DomainIndia VPS: Terraform and Ansible

ByDomain India Team·DomainIndia Engineering
5 min read24 Apr 20263 views
# Infrastructure as Code on DomainIndia VPS: Terraform and Ansible
TL;DR
Replace "SSH in and run commands" with reproducible, version-controlled infrastructure. Terraform provisions servers and resources; Ansible configures them. This guide shows a working stack for a DomainIndia VPS fleet — from zero to running services with a single terraform apply + ansible-playbook.
## Why IaC If you have 1 server, manual is fine. If you have 3+ (staging, production, disaster-recovery) or rebuild servers often, IaC is non-negotiable. Benefits: - Every change is a git commit (auditable) - Rebuild a lost server in 30 minutes, not 3 days - Same config across dev/staging/production — "works on staging" actually means something - Team-ready — two engineers don't step on each other ## Terraform vs Ansible
ToolRoleDeclarativeBest for
TerraformProvisioning (create VMs, DNS, load balancers)Yes — desired state"What infrastructure exists"
AnsibleConfiguration (install software, copy files, run scripts)Partial — playbooks are imperative"What's installed and configured"
Used together: Terraform creates the VPS, Ansible configures it. ## Part 1 — Terraform for DomainIndia DomainIndia VPS isn't a native Terraform provider, but you can manage DNS, Cloudflare, AWS S3 (backups), and remote VPS state via standard providers. For pure VM provisioning on DomainIndia you can use the `null_resource` + `remote-exec` pattern, or Order the VPS manually and manage IPs in Terraform as inputs. Example `main.tf` — Cloudflare DNS + AWS S3 backup bucket: ```hcl terraform { required_providers { cloudflare = { source = "cloudflare/cloudflare", version = "~> 4.0" } aws = { source = "hashicorp/aws", version = "~> 5.0" } } } provider "cloudflare" { api_token = var.cloudflare_token } provider "aws" { region = "ap-south-1" } resource "cloudflare_record" "api" { zone_id = var.cloudflare_zone_id name = "api" value = var.api_vps_ip # DomainIndia VPS IP type = "A" proxied = true ttl = 1 } resource "aws_s3_bucket" "backups" { bucket = "yourcompany-vps-backups" tags = { Environment = "production" } } resource "aws_s3_bucket_lifecycle_configuration" "backups" { bucket = aws_s3_bucket.backups.id rule { id = "expire-old" status = "Enabled" expiration { days = 90 } } } ``` `variables.tf`: ```hcl variable "cloudflare_token" { type = string; sensitive = true } variable "cloudflare_zone_id" { type = string } variable "api_vps_ip" { type = string } ``` `terraform.tfvars` (git-ignored): ``` cloudflare_token = "your-token" cloudflare_zone_id = "abc123" api_vps_ip = "65.109.X.X" ``` Apply: ```bash terraform init terraform plan terraform apply ``` ## Part 2 — Ansible for server configuration Ansible reads a YAML "playbook" and runs steps over SSH. No agent, no daemon — just SSH + Python. Install Ansible on your laptop: ```bash pip install ansible ``` Create `inventory.ini`: ```ini [web] api.yourcompany.com [web:vars] ansible_user=deploy ansible_ssh_private_key_file=~/.ssh/id_rsa ``` Create `site.yml`: ```yaml --- - name: Configure API server hosts: web become: yes vars: app_user: goapp app_dir: /home/goapp/app tasks: - name: Install essentials package: name: - nginx - certbot - python3-certbot-nginx - postgresql - fail2ban state: present - name: Create app user user: name: "{{ app_user }}" shell: /bin/bash home: "/home/{{ app_user }}" create_home: yes - name: Deploy nginx config template: src: templates/nginx.conf.j2 dest: /etc/nginx/conf.d/api.conf notify: reload nginx - name: Start services systemd: name: "{{ item }}" enabled: yes state: started loop: [nginx, postgresql, fail2ban] - name: Allow HTTP/HTTPS through firewall firewalld: service: "{{ item }}" permanent: yes immediate: yes state: enabled loop: [http, https] handlers: - name: reload nginx systemd: { name: nginx, state: reloaded } ``` Run: ```bash ansible-playbook -i inventory.ini site.yml ``` Every step is idempotent — run it 100 times, the end state is the same. ## Part 3 — Ansible Vault for secrets Never commit passwords in plaintext. Ansible Vault encrypts files: ```bash ansible-vault create group_vars/all/secrets.yml # opens editor, type: db_password: "s3cr3t!" api_token: "sk-..." ``` Reference in playbooks normally: `"{{ db_password }}"`. Run with `--ask-vault-pass`. Store the vault password in a file outside git: ```bash ansible-playbook -i inventory.ini site.yml --vault-password-file ~/.vault_pass ``` ## Part 4 — Common patterns **Pattern 1: Rolling deployment** ```yaml - hosts: web serial: 1 # one server at a time tasks: - name: Drain from load balancer uri: { url: "http://lb/drain/{{ inventory_hostname }}", method: POST } - name: Deploy new binary copy: { src: ./myapp, dest: "{{ app_dir }}/myapp", mode: 0755 } - name: Restart service systemd: { name: myapp, state: restarted } - name: Wait for health uri: { url: "http://localhost:8080/health", status_code: 200 } retries: 10 delay: 3 - name: Add back to load balancer uri: { url: "http://lb/activate/{{ inventory_hostname }}", method: POST } ``` **Pattern 2: Disaster recovery dry-run** ```bash # Destroy staging and rebuild from scratch terraform destroy -var-file=staging.tfvars terraform apply -var-file=staging.tfvars ansible-playbook -i inventory-staging.ini site.yml ``` ## GitOps: CI/CD for IaC Push changes → GitHub Actions → `terraform plan` → PR comment with diff → human review → merge → auto-apply. Example `.github/workflows/terraform.yml`: ```yaml on: [pull_request, push] jobs: terraform: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: hashicorp/setup-terraform@v3 - run: terraform init - run: terraform plan -no-color if: github.event_name == 'pull_request' - run: terraform apply -auto-approve if: github.ref == 'refs/heads/main' ``` Secrets (`TF_VAR_cloudflare_token` etc.) go in Repo Settings → Secrets. ## Common pitfalls ## FAQ
Q Do I need IaC for one VPS?

Not strictly. But writing Ansible playbooks for even a single server is good discipline — you document the setup, can rebuild quickly, and can grow to a fleet later.

Q Terraform vs Pulumi vs OpenTofu?

OpenTofu is a fork of Terraform (licence dispute), works identically. Pulumi uses real programming languages (TypeScript/Python) instead of HCL. For most teams, Terraform/OpenTofu is the simplest.

Q Ansible vs Puppet vs Chef?

Ansible has won the config-management space. Puppet/Chef are older, require agents, heavier to operate. For a DomainIndia VPS fleet, stick with Ansible.

Q Can I manage shared cPanel with IaC?

Limited. cPanel has a WHM API, but Terraform providers are community-maintained. For serious automation, go VPS.

Start your IaC journey with a VPS where you have full root access. Order VPS

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket

Still need help?

Our support team can assist you directly.

Submit Ticket