Skip to content

libops/cloud-compose

Repository files navigation

cloud-compose

Deploy Docker Compose projects to VMs. Provider-specific Terraform entrypoints under providers/ cover GCP, DigitalOcean, and Linode without loading unused cloud providers. Existing Debian/Ubuntu hosts can consume the same runtime contract through the Ansible role or Salt formula.

Template defaults live in templates/apps.json and are shared by Terraform, Ansible, and Salt. The default deployment shape is one app per VM or host; pass runtime.compose.projects when several apps should share the same machine.

Requirements

Name Version
terraform >= 1.2.4

Providers

No providers.

Modules

Name Source Version
digitalocean ./modules/digitalocean n/a
gcp ./modules/gcp n/a
linode ./modules/linode n/a

Resources

No resources.

Inputs

Name Description Type Default Required
name Deployment name. string n/a yes
cloud_provider Cloud provider to deploy to. Supported values are gcp, digitalocean, and linode. string "gcp" no
digitalocean DigitalOcean infrastructure settings.
object({
region = optional(string, "tor1")
tags = optional(list(string), ["cloud-compose"])

droplet = optional(object({
size = optional(string, "s-2vcpu-4gb")
image = optional(string, "ubuntu-24-04-x64")
ssh_keys = optional(list(string), [])
vpc_uuid = optional(string, null)
monitoring = optional(bool, true)
ipv6 = optional(bool, true)
backups = optional(bool, false)
}), {})

ssh = optional(object({
cloud_compose_keys = optional(list(string), [])
users = optional(map(list(string)), {})
}), {})

volumes = optional(object({
data_size_gb = optional(number, 50)
docker_volumes_size_gb = optional(number, 100)
}), {})

firewall = optional(object({
enabled = optional(bool, true)
ssh_source_addresses = optional(list(string), ["0.0.0.0/0", "::/0"])
web_source_addresses = optional(list(string), ["0.0.0.0/0", "::/0"])
}), {})
})
{} no
gcp Google Cloud infrastructure settings.
object({
project_id = optional(string, "")
project_number = optional(string, "")
region = optional(string, "us-east5")
zone = optional(string, "us-east5-b")

identity = optional(object({
vm_service_account_email = optional(string, "")
app_service_account_email = optional(string, "")
}), {})

instance = optional(object({
machine_type = optional(string, "n4-standard-2")
os = optional(string, "cos-125-19216-220-185")
production = optional(bool, false)
}), {})

disks = optional(object({
type = optional(string, "hyperdisk-balanced")
docker_volumes_size_gb = optional(number, 50)
}), {})

network = optional(object({
create = optional(bool, true)
name = optional(string, "")
subnetwork = optional(string, "")
ip_cidr_range = optional(string, "10.42.0.0/24")
power_button_allowed_ips = optional(list(string), [])
ssh_ipv4 = optional(list(string), [])
ssh_ipv6 = optional(list(string), [])
}), {})

snapshots = optional(object({
enabled = optional(bool, false)
}), {})

overlay = optional(object({
source_instance = optional(string, "")
volume_names = optional(list(string), [])
}), {})

cloud_init = optional(object({
initcmd = optional(list(string), [])
runcmd = optional(list(string), [])
}), {})

artifact_registry = optional(object({
repository = optional(string, "")
location = optional(string, "us")
}), {})

power_management = optional(object({
enabled = optional(bool, true)
frontend = optional(object({
image = string
port = optional(number, 8080)
cpu = optional(string, "1000m")
memory = optional(string, "1Gi")
}), null)
}), {})

rollout = optional(object({
enabled = optional(bool, false)
release_url = optional(string, "")
release_sha256 = optional(string, "")
port = optional(number, 8081)
jwks_uri = optional(string, "")
jwt_audience = optional(string, "")
custom_claims = optional(string, "")
allowed_ipv4 = optional(list(string), ["10.0.0.0/8"])
}), {})
})
{} no
linode Linode infrastructure settings.
object({
region = optional(string, "us-east")
tags = optional(list(string), ["cloud-compose"])

instance = optional(object({
type = optional(string, "g6-standard-2")
image = optional(string, "linode/ubuntu22.04")
authorized_keys = optional(list(string), [])
authorized_users = optional(list(string), [])
root_pass = optional(string, null)
private_ip = optional(bool, true)
backups_enabled = optional(bool, false)
watchdog_enabled = optional(bool, true)
}), {})

ssh = optional(object({
cloud_compose_keys = optional(list(string), [])
users = optional(map(list(string)), {})
}), {})

volumes = optional(object({
data_size_gb = optional(number, 50)
docker_volumes_size_gb = optional(number, 100)
}), {})

firewall = optional(object({
enabled = optional(bool, true)
ssh_source_ipv4 = optional(list(string), ["0.0.0.0/0"])
ssh_source_ipv6 = optional(list(string), ["::/0"])
web_source_ipv4 = optional(list(string), ["0.0.0.0/0"])
web_source_ipv6 = optional(list(string), ["::/0"])
}), {})
})
{} no
runtime Provider-neutral compose/runtime settings.
object({
rootfs = optional(string, "")
rootfs_archive_url = optional(string, "")
rootfs_archive_sha256 = optional(string, "")
users = optional(map(list(string)), {})

compose = optional(object({
primary = optional(string, "")
ingress_port = optional(number, 80)
ingress = optional(object({
letsencrypt = optional(bool, false)
bot_mitigation = optional(bool, false)
mode = optional(string, "")
domain = optional(string, "")
acme_email = optional(string, "")
trusted_ips = optional(list(string), [])
max_upload_size = optional(string, "")
upload_timeout = optional(string, "")
}), {})
repo = optional(string, "")
branch = optional(string, "main")
projects = optional(map(object({
docker_compose_repo = string
docker_compose_branch = optional(string)
project_dir = optional(string)
compose_project_name = optional(string)
ingress_port = optional(number)
ingress = optional(object({
letsencrypt = optional(bool)
bot_mitigation = optional(bool)
mode = optional(string)
domain = optional(string)
acme_email = optional(string)
trusted_ips = optional(list(string))
max_upload_size = optional(string)
upload_timeout = optional(string)
}), {})
sitectl_context_name = optional(string)
sitectl_plugin = optional(string)
sitectl_environment = optional(string)
sitectl_packages = optional(list(string), [])
sitectl_verify_args = optional(list(string), [])
docker_compose_init = optional(list(string))
docker_compose_up = optional(list(string))
docker_compose_down = optional(list(string))
docker_compose_rollout = optional(list(string))
})), {})
init = optional(list(string), [
"sitectl config set-context "$${SITECTL_CONTEXT_NAME}" --type local --project-dir "$${DOCKER_COMPOSE_DIR}" --site "$${CLOUD_COMPOSE_INSTANCE_NAME}" --plugin "$${SITECTL_PLUGIN}" --environment "$${SITECTL_ENVIRONMENT}" --project-name "$${CLOUD_COMPOSE_INSTANCE_NAME}" --compose-project-name "$${COMPOSE_PROJECT_NAME}" --docker-socket /var/run/docker.sock --env-file .env --default"
])
up = optional(list(string), [
"sitectl compose --context "$${SITECTL_CONTEXT_NAME}" up -d --remove-orphans",
"sitectl healthcheck --context "$${SITECTL_CONTEXT_NAME}" --persist --timeout "$${SITECTL_HEALTHCHECK_TIMEOUT}" --interval "$${SITECTL_HEALTHCHECK_INTERVAL}"",
"if [ "$${SITECTL_ENVIRONMENT}" != "production" ]; then sitectl verify --context "$${SITECTL_CONTEXT_NAME}" $${SITECTL_VERIFY_ARGS:-}; fi"
])
down = optional(list(string), [
"sitectl compose --context "$${SITECTL_CONTEXT_NAME}" down"
])
rollout = optional(list(string), [
"TARGET_REF="$${GIT_REF:-$${GIT_BRANCH:-$${DOCKER_COMPOSE_BRANCH:-main}}}"",
"if [ -x ./scripts/rollout.sh ]; then ./scripts/rollout.sh; else sitectl deploy --context "$${SITECTL_CONTEXT_NAME}" --branch "$TARGET_REF"; fi",
"sitectl healthcheck --context "$${SITECTL_CONTEXT_NAME}" --persist --timeout "$${SITECTL_HEALTHCHECK_TIMEOUT}" --interval "$${SITECTL_HEALTHCHECK_INTERVAL}"",
"if [ "$${SITECTL_ENVIRONMENT}" != "production" ]; then sitectl verify --context "$${SITECTL_CONTEXT_NAME}" $${SITECTL_VERIFY_ARGS:-}; fi"
])
}), {})

sitectl = optional(object({
packages = optional(list(string), ["sitectl"])
version = optional(string, "latest")
context_name = optional(string, "")
plugin = optional(string, "core")
environment = optional(string, "production")
healthcheck_timeout = optional(string, "20m")
healthcheck_interval = optional(string, "15s")
verify_args = optional(list(string), [])
}), {})

docker = optional(object({
# renovate: datasource=github-releases depName=docker-compose packageName=docker/compose versioning=semver
compose_version = optional(string, "v5.2.0")
# renovate: datasource=github-releases depName=docker-buildx packageName=docker/buildx versioning=semver
buildx_version = optional(string, "v0.35.0")
}), {})

managed_runtime = optional(object({
enabled = optional(bool, true)
internal_services_enabled = optional(bool, true)
internal_services_auto_update = optional(bool, true)
lightsout_image = optional(string, "ghcr.io/libops/lightsout:main")
cap_image = optional(string, "ghcr.io/libops/cap:main")
cadvisor_image = optional(string, "ghcr.io/google/cadvisor:v0.57.0@sha256:e75bdb03b74b0b6995f208f166fead2e6e555dde73e44200113bb26f41b1981d")
artifacts = optional(list(object({
name = string
url = string
sha256 = string
path = string
mode = optional(string, "0755")
owner = optional(string, "root")
group = optional(string, "root")
restart = optional(string, "")
})), [])
}), {})

vault = optional(object({
addr = optional(string, "")
namespace = optional(string, "")
role = optional(string, "")
agent_enabled = optional(bool, false)
auth_method = optional(string, "gcp-iam")
gcp_auth_mount_path = optional(string, "auth/gcp")
agent_token_path = optional(string, "/mnt/disks/data/vault/token")
agent_additional_config = optional(string, "")
agent_templates = optional(list(object({
destination = string
contents = string
perms = optional(string, "0640")
command = optional(string, "")
})), [])
}), {})

extra_env = optional(map(string), {})
})
{} no
template Optional compose template preset. Supported values are archivesspace, ojs, isle, drupal, wp, omeka-s, and omeka-classic. Explicit runtime settings override preset defaults. string "" no

Outputs

Name Description
appGsa The Google Service Account the app can use for app-scoped auth.
backend Backend service ID for attaching Cloud Run ingress to an external HTTPS load balancer.
cloud_provider Selected cloud provider.
compose_projects Normalized compose project manifest.
external_ip Selected provider VM public IPv4 address.
instance Selected provider VM instance details.
instance_id Selected provider VM instance ID.
internal_ip Selected provider VM private IPv4 address.
primary_compose_project Normalized primary compose project.
rollout Optional rollout API endpoint details.
serviceGsa The Google Service Account internal services run as.
template Selected compose template preset.
urls Cloud Run ingress URLs by region.
volumes Selected provider persistent volume details where available.

About

Deploy a docker compose project to a Google Cloud Compute Instance

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors