# Deploy the full-stack application template with OpenTofu

Source: https://docs.quake.ai/resources/deployments/deploy-full-stack-app-template
Markdown: https://docs.quake.ai/resources/deployments/deploy-full-stack-app-template.md
> Stand up a three-tier web, app, and MariaDB stack on a private network with one floating IP on the web tier using the full-stack-app OpenTofu template.

---

# Deploy the full-stack application template with OpenTofu

Stand up a three-tier stack (Nginx web, Python app on port 8080, MariaDB with a Cinder data volume) on a private subnet using the [validated OpenTofu template](/docs/platform/validation#how-infrastructure-templates-are-checked) `full-stack-app`. One floating IP on the web tier exposes HTTP; the app and database tiers stay on private addresses only.

<PricingCompanion
  components={[
    { kind: "template", slug: "full-stack-app", required: true },
  ]}
/>

<Figure size="md" caption="Full-stack topology: web, app, and database tiers on a private network with one floating IP on the web tier">

```d2
direction: right

cloud: Quake AI {
  fip: Floating IP\nport 80
  private: Private network\n192.168.50.0/24 {
    web: Web tier\nNginx
    app: App tier\nPython :8080
    db: DB tier\nMariaDB :3306
  }
  vol: Block volume\n50 GB\n/var/lib/mysql
  router: Router\nto PublicStatic
}

cloud.fip -> cloud.private.web
cloud.private.web -> cloud.private.app: :8080
cloud.private.app -> cloud.private.db: :3306
cloud.vol -> cloud.private.db
cloud.router -> cloud.private
```

</Figure>

## Prerequisites

You need:

- A Quake AI account with [application credentials](/docs/tools/generate-app-credentials)
- OpenTofu 1.6.0 or later ([installation guide](https://opentofu.org/docs/intro/install/))
- OpenStack credentials sourced into the shell (`source openrc.sh`). See [the OpenStack CLI guide](/docs/tools/openstack-cli).
- An SSH key pair already uploaded to the project. See [Add an SSH key](/docs/tools/add-ssh-key).
- A copy of the `full-stack-app` template from [the template reference page](/resources/iac-templates/full-stack-app)
- Enough project quota for three instances at the default flavors, one 50 GB block volume, and one floating IP

## Step 1: Configure variables

Copy `terraform.tfvars.example` to `terraform.tfvars` and set:

```hcl
key_name    = "YOUR_KEY_NAME"
db_password = "CHOOSE_A_STRONG_PASSWORD"
```

Defaults for flavors, volume size, and network CIDR are documented on the [Full-Stack Application](/resources/iac-templates/full-stack-app) reference page.

## Step 2: Apply the template

From the template directory, run:

```bash
tofu init
tofu plan
tofu apply
```

Type `yes` when prompted. Cloud-init on all three instances can take several more minutes after `tofu apply` returns.

When the run finishes, note `web_floating_ip`, `app_private_ip`, and `db_private_ip` from the outputs.

## Step 3: Verify the web tier reaches the app and database tiers

Fetch the public endpoint and request the root path:

```bash
WEB=$(tofu output -raw web_floating_ip)
curl -s "http://${WEB}/"
```

When all three tiers are ready, the response body reads:

```text
app tier OK; db says: full-stack-app database tier online
```

If MariaDB or the app service is still starting, you may see a `503` response. Wait two or three minutes and run the `curl` command again.

Optional: SSH to the web instance and confirm Nginx forwards to the app tier:

```bash
ssh -i YOUR_PRIVATE_KEY_PATH ubuntu@${WEB}
systemctl is-active nginx
grep proxy_pass /etc/nginx/sites-available/default
```

## Next steps

- [Full-Stack Application template](/resources/iac-templates/full-stack-app)
- [Three-Tier Application template](/resources/iac-templates/three-tier-app)
- [Web applications](/resources/solutions/web-applications)

## Clean up

Run `tofu destroy` from the project directory when finished. Type `yes` to confirm. Verify in the Console that all instances, the data volume, and the floating IP are gone.
