All case studiesApplication Delivery

Multi-Cloud Application Delivery

From four provisioned VMs to the same app running over HTTPS on all of them — one apply, one shared bootstrap, four clouds.

Terraformcloud-initsystemdMulti-Cloud

The challenge

Provisioning four VMs is only half the job. Getting the same application running identically on all of them — despite four different cloud APIs, auth models, package managers, and firewall systems — is where most multi-cloud efforts fall apart.

Our approach

One root Terraform module composes four per-cloud child modules. A single identical bootstrap script is read once into a Terraform local and passed as instance startup data to all four clouds; each child wires it into its own mechanism — plain user-data on AWS and GCP, base64 custom-data on Azure and OCI. The result is one source of truth for how the app comes up, everywhere.

The bootstrap script is deliberately portable: it detects the package manager, installs the runtime, creates a virtual environment, initialises data within a bounded memory budget, generates a self-signed certificate, and installs a systemd unit that serves the app directly over HTTPS. Firewall rules are applied with whichever tool the OS uses.

Root modulecloud-init.shread once → localuser_data ×44 child modulesper-cloud wiringAWS VM · gunicorn :443Azure / GCP VM · gunicorn :443OCI VM · gunicorn :443

Technical specifics

  • A single cloud-init script (90+ lines) that branches on dnf vs apt-get and installs Python, pip, git, and openssl.
  • A Python virtual environment, dependencies plus gunicorn, and a bounded data-initialisation step sized to fit 1 GB of RAM.
  • A self-signed RSA-2048 TLS certificate (10-year validity) and a systemd unit running gunicorn bound to 0.0.0.0:443.
  • Firewall configured per OS — iptables on Ubuntu/OCI, firewall-cmd on the RHEL family — for ports 80 and 443.
  • Independent SQLite per VM (no shared state); outputs include app_urls, public_ips, and ssh_commands.

Example configuration

One bootstrap script is read once and fed to every cloud; a systemd unit serves the app over HTTPS.

main.tf — shared bootstraphcl
locals { app_bootstrap = file("${path.module}/cloud-init.sh") }

module "aws"   { source = "./modules/aws"   user_data = local.app_bootstrap }
module "gcp"   { source = "./modules/gcp"   user_data = local.app_bootstrap }
# Azure & OCI expect base64:
module "azure" { source = "./modules/azure" user_data = base64encode(local.app_bootstrap) }
module "oci"   { source = "./modules/oci"   user_data = base64encode(local.app_bootstrap) }
cloud-init.sh (excerpt)bash
#!/usr/bin/env bash
set -euo pipefail
if command -v dnf >/dev/null; then PKG=dnf; else PKG="apt-get"; fi
"${PKG}" install -y python3 python3-pip git openssl

git clone --depth 1 https://github.com/beknar/poli-tracker.git /opt/poli-tracker
cd /opt/poli-tracker && python3 -m venv .venv
.venv/bin/pip install -r requirements.txt gunicorn

# self-signed TLS, then serve over 443 via systemd
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
  -keyout tls.key -out tls.crt -subj "/CN=${APP_DOMAIN:-app.example.com}"

cat >/etc/systemd/system/app.service <<'UNIT'
[Service]
ExecStart=/opt/poli-tracker/.venv/bin/gunicorn -b 0.0.0.0:443 -w 1 \
  --certfile /opt/poli-tracker/tls.crt --keyfile /opt/poli-tracker/tls.key app:app
[Install]
WantedBy=multi-user.target
UNIT
systemctl enable --now app.service

Outcome

One apply provisions four identical app servers across four clouds, each serving the application over HTTPS on port 443 within minutes — reproducible, reviewable, and with no secrets in version control. Teardown is equally simple.

Have a similar challenge?

We deliver this work end to end — from first commit to a secure, live deploy. Let’s talk about yours.