Skip to content

Coolify host

Template

Coolify host

This pattern composes Compute, Network, and Block Storage into a self-hosted deploy platform you run on infrastructure you control.

What this template does#

Provisions a single instance running Coolify, an open-source deploy platform (a self-hosted alternative to Vercel, Netlify, or Heroku). You connect a git repository and Coolify builds, deploys, and manages your apps, with Postgres and Redis add-ons it provisions for you:

  • Compute instance that runs Coolify in Docker, sized above Coolify's baseline of 2 vCPU and 2 GiB RAM
  • Private network, subnet, router, port, and security group; a floating IP for public access
  • A block volume mounted at /var/lib/docker, so image layers, container volumes, and deployed-app data live on a volume you can grow rather than on the boot disk
  • cloud-init runs Coolify's official installer, which bootstraps Docker Engine 24+ and generates its own secrets under /data/coolify

Coolify gives you a git-push build-and-deploy loop on a VM you own. The platform already runs Coolify internally for Qdrant and Umami, so this template uses tooling the platform operates day to day.

No credential ships with this template. The installer generates its own secrets on first boot, and you set the admin account the first time you open the dashboard.

Parameters#

ParameterDescriptionDefault
key_nameSSH keypair name (must already exist)No default
flavor_nameInstance size (Coolify baseline is 2 vCPU / 2 GiB)s1a.medium
image_nameOperating system imageUbuntu-24.04
app_nameDisplay name prefix for resourcescoolify
volume_sizeBlock volume size in GiB, mounted at /var/lib/docker50
external_networkExternal network for floating IP allocationPublicStatic
private_cidrCIDR for the private subnet10.30.0.0/24
dashboard_allowed_cidrCIDR allowed to reach the dashboard on port 800010.30.0.0/24

Dashboard access and security#

The dashboard listens on port 8000 over plain HTTP. The security group restricts 8000 to dashboard_allowed_cidr, which defaults to the private network only, so the raw dashboard stays off the public internet. Reach it one of three ways:

  • Set a domain in Coolify and let its built-in proxy serve the dashboard over HTTPS on 443. Point the domain's DNS A record at the floating IP. This is the recommended path for routine admin access.
  • Tunnel over SSH: ssh -L 8000:localhost:8000 user@FLOATING_IP, then open http://localhost:8000.
  • Set dashboard_allowed_cidr to YOUR_IP/32 to reach port 8000 directly from one address.

Ports 80 and 443 stay open to the internet because they carry the apps Coolify deploys and the dashboard when you serve it over a domain.

When to use this pattern#

Run a deploy platform with a dashboard, branch previews, and managed add-ons on a VM you operate, without wiring those pieces yourself. Coolify maps a git push to a build and a deploy, the same shape a quake.yaml launch manifest declares, which makes it a natural target for the launch handoff packet.

For a single containerized app without the platform layer, use the Next.js app template or the Containerized app template. For a standalone datastore the deployed apps consume, see self-managed PostgreSQL or the Redis / Valkey cache.

Estimated cost#

Monthly cost estimate

Pricing calculator ↗

Sized as a custom package on shared vCPU.

Starting template$34.40/mo

Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.

What each resource is for

Coolify host

s1a.medium · 4 shared vCPU, 4 GiB RAM, 0.5 Gbps

Runs Coolify in Docker, along with the Postgres and Redis add-ons it provisions for the apps you deploy.

Coolify's baseline is 2 vCPU and 2 GiB RAM. The default size doubles that to leave headroom for image builds and the add-on containers.

$33.00/mo

Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.

Included in baseline

s1a.medium

4 shared vCPU, 4 GiB RAM, 0.5 Gbps

$33.00

Compute + RAM rate basis

4 vCPU + 4 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.

—

Block storage (80 GiB)

80 GiB at $0.08/GiB/mo

$6.40

Public IP (included)

1 included with the custom package

$0.00

Package promotional discount

Flat −$5.00/mo on the custom package (same promotion as named plans).

$-5.00

Included at no charge

These line items are zero on Quake AI. Many other providers meter them separately.

Data transfer (inbound and outbound)

Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.

AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.

Learn more
$0.00

Private networking

Private networks, subnets, Neutron routers, and security groups are included with the plan.

VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.

$0.00

Control-plane API requests

OpenStack API calls for provisioning and management are included.

Some managed services on other clouds meter API calls or charge for premium control-plane features.

$0.00

Pricing data last validated: . For current rates, check quake.ai/pricing.

Template source#

7 files. Download the zip or expand to copy any file.Download coolify-host.zip
Show source (7 files)
main.tfHCL
data "openstack_images_image_v2" "os" {
  name        = var.image_name
  most_recent = true
}

data "openstack_networking_network_v2" "external" {
  name = var.external_network
}

resource "openstack_networking_network_v2" "private" {
  name           = "${var.app_name}-net"
  admin_state_up = true
}

resource "openstack_networking_subnet_v2" "private" {
  name            = "${var.app_name}-subnet"
  network_id      = openstack_networking_network_v2.private.id
  cidr            = var.private_cidr
  ip_version      = 4
  dns_nameservers = ["1.1.1.1", "8.8.8.8"]
}

resource "openstack_networking_router_v2" "main" {
  name                = "${var.app_name}-router"
  external_network_id = data.openstack_networking_network_v2.external.id
}

resource "openstack_networking_router_interface_v2" "private" {
  router_id = openstack_networking_router_v2.main.id
  subnet_id = openstack_networking_subnet_v2.private.id
}

resource "openstack_networking_secgroup_v2" "coolify" {
  name        = "${var.app_name}-sg"
  description = "SSH and HTTP/HTTPS for deployed apps; dashboard port 8000 restricted"
}

resource "openstack_networking_secgroup_rule_v2" "ssh" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 22
  port_range_max    = 22
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.coolify.id
}

# 80 and 443 carry the apps Coolify deploys and the dashboard when served over a
# domain with automatic TLS through Coolify's built-in proxy.
resource "openstack_networking_secgroup_rule_v2" "http" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 80
  port_range_max    = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.coolify.id
}

resource "openstack_networking_secgroup_rule_v2" "https" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 443
  port_range_max    = 443
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.coolify.id
}

# Raw dashboard HTTP on 8000 is restricted to dashboard_allowed_cidr (private
# network by default). Prefer a domain with TLS on 443 for routine admin access.
resource "openstack_networking_secgroup_rule_v2" "dashboard" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 8000
  port_range_max    = 8000
  remote_ip_prefix  = var.dashboard_allowed_cidr
  security_group_id = openstack_networking_secgroup_v2.coolify.id
}

resource "openstack_networking_port_v2" "coolify" {
  name               = "${var.app_name}-port"
  network_id         = openstack_networking_network_v2.private.id
  security_group_ids = [openstack_networking_secgroup_v2.coolify.id]

  fixed_ip {
    subnet_id = openstack_networking_subnet_v2.private.id
  }

  depends_on = [openstack_networking_router_interface_v2.private]
}

resource "openstack_blockstorage_volume_v3" "data" {
  name = "${var.app_name}-data"
  size = var.volume_size
}

resource "openstack_compute_instance_v2" "coolify" {
  name        = var.app_name
  flavor_name = var.flavor_name
  key_pair    = var.key_name

  user_data = file("${path.module}/cloud-init/coolify.yaml")

  block_device {
    uuid                  = data.openstack_images_image_v2.os.id
    source_type           = "image"
    destination_type      = "volume"
    volume_size           = 30
    boot_index            = 0
    delete_on_termination = true
  }

  network {
    port = openstack_networking_port_v2.coolify.id
  }
}

resource "openstack_compute_volume_attach_v2" "data" {
  instance_id = openstack_compute_instance_v2.coolify.id
  volume_id   = openstack_blockstorage_volume_v3.data.id
}

resource "openstack_networking_floatingip_v2" "coolify" {
  pool = var.external_network
}

resource "openstack_networking_floatingip_associate_v2" "coolify" {
  floating_ip = openstack_networking_floatingip_v2.coolify.address
  port_id     = openstack_networking_port_v2.coolify.id
}
variables.tfHCL
variable "key_name" {
  description = "SSH keypair name (must already exist in your project)"
  type        = string
}

variable "flavor_name" {
  description = "Instance size. Coolify's baseline is 2 vCPU and 2 GiB RAM; the default leaves headroom for Coolify plus a few small deployed apps. Size up for heavier workloads."
  type        = string
  default     = "s1a.medium"
}

variable "image_name" {
  description = "Operating system image. Coolify supports Debian- and RHEL-family distributions; Ubuntu 24.04 is the recommended base."
  type        = string
  default     = "Ubuntu-24.04"
}

variable "app_name" {
  description = "Display name prefix for compute and network resources"
  type        = string
  default     = "coolify"
}

variable "volume_size" {
  description = "Block volume size in GiB for Docker image layers, container volumes, and deployed-app data. The volume is mounted at /var/lib/docker so all of Coolify's growing storage lives here. Coolify recommends 30+ GiB for images; the default leaves room for several apps."
  type        = number
  default     = 50
}

variable "external_network" {
  description = "Shared external network for router gateway and floating IPs; defaults to PublicStatic (persisted FIP / production pattern). Override with PublicEphemeral for ephemeral demos."
  type        = string
  default     = "PublicStatic"
}

variable "private_cidr" {
  description = "CIDR for the private tenant network the instance lives in"
  type        = string
  default     = "10.30.0.0/24"
}

variable "dashboard_allowed_cidr" {
  description = "CIDR allowed to reach the Coolify dashboard on port 8000. Defaults to the private network only, so the raw dashboard is not exposed to the public internet. Reach it over an SSH tunnel, or (recommended) set a domain in Coolify and serve the dashboard over HTTPS on 443. To allow direct access from your workstation, set this to YOUR_IP/32."
  type        = string
  default     = "10.30.0.0/24"
}
outputs.tfHCL
output "instance_id" {
  description = "ID of the compute instance running Coolify"
  value       = openstack_compute_instance_v2.coolify.id
}

output "floating_ip" {
  description = "Public floating IP address of the Coolify host"
  value       = openstack_networking_floatingip_v2.coolify.address
}

output "private_ip" {
  description = "Private IP address of the instance"
  value       = openstack_compute_instance_v2.coolify.access_ip_v4
}

output "dashboard_url" {
  description = "Coolify dashboard URL on port 8000. Reachable from dashboard_allowed_cidr (the private network by default; tunnel over SSH, or set a domain in Coolify and use HTTPS on 443)."
  value       = "http://${openstack_networking_floatingip_v2.coolify.address}:8000"
}
versions.tfHCL
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.0"
    }
  }
}

provider "openstack" {}
terraform.tfvars.exampleHCL
# Required: SSH keypair must already exist in your project
key_name = "YOUR_KEY_NAME"

# Recommended: restrict the dashboard (port 8000) to your workstation IP.
# Leave unset to keep 8000 reachable only from the private network and tunnel
# over SSH, or set a domain in Coolify and use HTTPS on 443.
# dashboard_allowed_cidr = "203.0.113.10/32"

# flavor_name = "s1a.medium"
# image_name = "Ubuntu-24.04"
# app_name = "coolify"
# volume_size = 50
# external_network = "PublicStatic"
# private_cidr = "10.30.0.0/24"
cloud-init/coolify.yamlYAML
#cloud-config
package_update: true
packages:
  - ca-certificates
  - curl
runcmd:
  - |
    set -e
    # The data volume attaches as /dev/sdb on this platform (not /dev/vdb).
    # Mount it at /var/lib/docker before Docker is installed so every image
    # layer, container volume, and deployed-app volume lives on the resizable
    # volume rather than the boot disk.
    DEV=/dev/sdb
    for i in $(seq 1 30); do [ -b "$DEV" ] && break; sleep 5; done
    if ! blkid "$DEV" >/dev/null 2>&1; then mkfs.ext4 -F -L coolifydata "$DEV"; fi
    mkdir -p /var/lib/docker
    mount "$DEV" /var/lib/docker
    grep -q "$DEV" /etc/fstab || echo "$DEV /var/lib/docker ext4 defaults,nofail 0 2" >> /etc/fstab
    # The Coolify installer bootstraps Docker Engine 24+, generates its own
    # secrets under /data/coolify, and starts the dashboard on port 8000.
    # No credential is supplied here: the admin account is set on first visit
    # to the dashboard.
    curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
README.mdMarkdown
# Coolify host

Single compute instance running [Coolify](https://coolify.io), a self-hosted deploy platform (a self-hosted alternative to Vercel, Netlify, or Heroku) on infrastructure you control. After apply, you push code or connect a git repository and Coolify builds, deploys, and manages your apps, with Postgres and Redis add-ons it provisions for you.


**Network class:** production — `external_network` defaults to `PublicStatic` for persisted floating IPs and multi-tier stacks; override with `PublicEphemeral` for ephemeral demos.

The instance provisions a private network, a floating IP, and a block volume mounted at `/var/lib/docker` so image layers, container volumes, and deployed-app data live on a resizable volume. cloud-init runs Coolify's official installer, which bootstraps Docker Engine and generates its own credentials on first boot.

## Where this fits

Coolify is the git-push baseline for vibecoders: it runs the build-and-deploy loop on a VM you own, rather than on a third-party managed cloud. It is often a better fit than raw OpenTofu when you want a dashboard, branch previews, and managed add-ons without wiring them yourself. The platform already runs Coolify internally (for Qdrant and Umami), so this template dogfoods existing tooling.

## Prerequisites

- OpenTofu >= 1.6.0 or Terraform >= 1.6.0
- Quake AI account with OpenStack credentials
- An existing SSH keypair in your project (the value of `key_name` must match that keypair)

## Resource baseline

Coolify's minimum is 2 vCPU, 2 GiB RAM, and 30+ GiB of storage. The default `s1a.medium` flavor (4 shared vCPU, 4 GiB RAM) leaves headroom for Coolify plus a few small apps. Size up for heavier workloads.

## Usage

1. Clone or copy this template directory
2. Copy `terraform.tfvars.example` to `terraform.tfvars` and fill in your values
3. Source your OpenStack credentials: `source openrc.sh`
4. Initialize: `tofu init`
5. Preview: `tofu plan`
6. Apply: `tofu apply`

After apply, the Coolify installer takes a few minutes to finish on first boot. Then open `dashboard_url` from the outputs and complete the first-run setup, which creates your admin account. No credential ships with this template.

## Dashboard access and security

The dashboard listens on port 8000 over plain HTTP. The security group restricts 8000 to `dashboard_allowed_cidr`, which defaults to the private network only, so the raw dashboard is not exposed to the public internet. Choose one of:

- **Recommended:** set a domain in Coolify and let its built-in proxy serve the dashboard over HTTPS on 443. Point the domain's DNS A record at `floating_ip`.
- **SSH tunnel:** `ssh -L 8000:localhost:8000 user@<floating_ip>`, then open `http://localhost:8000`.
- **Direct, scoped:** set `dashboard_allowed_cidr` to your workstation IP (`YOUR_IP/32`) to reach 8000 directly from one address.

Ports 80 and 443 stay open to the internet because they carry the apps Coolify deploys and the dashboard when you serve it over a domain.

## How the instance is provisioned

cloud-init:

1. Mounts the data volume at `/var/lib/docker` (formatting it on first boot) and adds an `/etc/fstab` entry so it persists across reboots.
2. Runs `curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash`, which installs Docker Engine 24+, generates Coolify's secrets under `/data/coolify`, and starts the dashboard.

## Variables

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key_name` | string | yes | n/a | SSH keypair name (must already exist in your project) |
| `flavor_name` | string | no | `s1a.medium` | Instance size (Coolify baseline is 2 vCPU / 2 GiB) |
| `image_name` | string | no | `Ubuntu-24.04` | Operating system image |
| `app_name` | string | no | `coolify` | Display name prefix for resources |
| `volume_size` | number | no | `50` | Block volume size in GiB, mounted at `/var/lib/docker` |
| `external_network` | string | no | `PublicStatic` | Persisted FIP / production default; override with `PublicEphemeral` for demos |
| `private_cidr` | string | no | `10.30.0.0/24` | CIDR for the private subnet |
| `dashboard_allowed_cidr` | string | no | `10.30.0.0/24` | CIDR allowed to reach the dashboard on port 8000 |

## Outputs

| Name | Description |
| --- | --- |
| `floating_ip` | Public floating IP assigned to the instance |
| `private_ip` | Private IP address of the instance |
| `dashboard_url` | Coolify dashboard URL on port 8000 |
| `instance_id` | Compute instance ID |

## Scope

This is a single-VM Coolify host that you operate, not a managed control plane. It is CPU-only and runs in one region; there is no native CDN. For high availability, run multiple hosts and put a load balancer in front, or use Coolify's multi-server features against additional instances.

## Documentation

See also: [Next.js app template](/resources/iac-templates/nextjs-app), [Containerized app template](/resources/iac-templates/containerized-app)
Resources, parameters, and variables
Provisions
Parameterized by
Variables
  • key_namerequired
  • flavor_name="s1a.medium"
  • image_name="Ubuntu-24.04"
  • app_name="coolify"
  • volume_size=50
  • external_network="PublicStatic"
  • private_cidr="10.30.0.0/24"
  • dashboard_allowed_cidr="10.30.0.0/24"

Customize this pattern#

See also#

Usage Guidelines

The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.

For the full policy, see Usage Guidelines.

Was this page helpful?