Installation

Note

AI Context

This page documents how to deploy a full VoIPBin platform on your own Google Cloud Platform project using the official installer at https://github.com/voipbin/install. It is intended for operators running their own VoIPBin instance, not for customers of the hosted service at https://voipbin.net.

VoIPBin is an opensource CPaaS platform. The complete stack, including the SIP edge (Kamailio, RTPEngine), the Asterisk media layer, all backend microservices, the database, the message bus, and the admin/talk/meet frontends, can be installed into a Google Cloud Platform project with a single CLI.

This section walks through what you need, the three commands that perform the install, what configuration the installer generates, and which environment variables you typically need to adjust before the platform is ready for real traffic.

Overview

The installer is an eight-stage pipeline driven by the voipbin-install CLI.

The eight stages run in order when you execute ./voipbin-install apply:

  1. terraform_init initializes the Terraform backend (GCS state bucket) and downloads providers.

  2. reconcile_imports detects any GCP resources that already exist outside Terraform state and imports them, preventing 409 conflicts on resume.

  3. terraform_apply provisions GCP infrastructure: a custom VPC, GKE cluster, Cloud SQL (MySQL 8.0), Kamailio and RTPEngine VMs, DNS zone, load balancers, KMS key ring for SOPS, and GCS buckets.

  4. reconcile_outputs reads Terraform outputs (IPs, connection names) into config.yaml so later stages can consume them.

  5. k8s_apply deploys VoIPBin services to the GKE cluster using manifests under k8s/, with placeholders substituted from Terraform outputs and SOPS-decrypted secrets.

  6. reconcile_k8s_outputs reads Kubernetes load balancer IPs into config.yaml.

  7. cert_provision issues Kamailio TLS certificates.

  8. ansible_run configures Kamailio and RTPEngine VMs over an IAP tunnel and renders their Docker Compose .env files from templates.

The pipeline is resumable: if a stage fails, fix the issue and rerun ./voipbin-install apply; it continues from where it left off. Run a single stage with ./voipbin-install apply --stage <name> (for example --stage ansible_run).

What gets deployed

A successful install produces:

  • VPC and connectivity: custom VPC 10.0.0.0/16, Cloud NAT with a static IP for outbound, Cloud Router, eight firewall rules covering SIP, RTP, IAP SSH, GKE internal, and health checks.

  • GKE cluster: zonal or regional, 2 nodes of n1-standard-2 by default, private nodes, shielded instances, COS_CONTAINERD image, REGULAR release channel.

  • Kamailio VM: 1x f1-micro by default, SIP/TLS/WSS proxy reached through an external network load balancer.

  • RTPEngine VM: 1x f1-micro by default, RTP media relay with a static external IP for direct media paths.

  • Cloud SQL MySQL 8.0: db-f1-micro instance, SSL required, daily automated backups, Cloud SQL Proxy deployed as a sidecar in GKE.

  • Kubernetes workloads: backend microservices in the bin-manager namespace, Asterisk in voip, Redis, RabbitMQ, ClickHouse, and Cloud SQL Proxy in infrastructure, plus three frontend apps (square-admin, square-talk, square-meet) in the square-manager namespace.

  • DNS records: six A records are required. With example.com as your domain: api.example.com, hook.example.com, admin.example.com, talk.example.com, meet.example.com, and sip.example.com. The first five point at the GKE load balancer IP and the last points at the Kamailio external load balancer IP.

Cost

Estimated monthly costs in us-central1 (on-demand, list price). Costs vary by region. You are responsible for all charges incurred on your GCP project.

Minimum config (gke_node_count=1, kamailio_count=1, rtpengine_count=1):

Resource

Cost/mo (USD)

GKE Control Plane

$0 zonal, ~$74 regional

GKE Node (1x n1-standard-2 + 100 GB disk)

~$213

Kamailio VM (1x f1-micro + 30 GB disk)

~$7

RTPEngine VM (1x f1-micro + 30 GB disk)

~$7

Cloud SQL MySQL db-f1-micro + 10 GB SSD

~$13

Static External IPs (8x in-use)

~$23

Load Balancers (Kamailio NLB 3 rules + K8s LB 5 services)

~$146

Cloud NAT gateway

~$32

Cloud DNS, GCS, KMS

~$1

Total

~$442 zonal / ~$516 regional

Default config (gke_node_count=2, kamailio_count=2, rtpengine_count=2):

Resource

Cost/mo (USD)

GKE Control Plane

$0 zonal, ~$74 regional

GKE Nodes (2x n1-standard-2 + 100 GB disks)

~$426

Kamailio VMs (2x f1-micro + 30 GB disks)

~$14

RTPEngine VMs (2x f1-micro + 30 GB disks)

~$14

Cloud SQL MySQL db-f1-micro + 10 GB SSD

~$13

Static External IPs (9x in-use)

~$26

Load Balancers (Kamailio NLB 3 rules + K8s LB 5 services)

~$146

Cloud NAT gateway

~$32

Cloud DNS, GCS, KMS

~$1

Total

~$672 zonal / ~$746 regional

These figures are on-demand list prices and do not include committed-use discounts, data egress, or usage beyond the included free tiers.

Warning

./voipbin-install destroy is irreversible. It permanently deletes every resource the installer created, including the Cloud SQL instance and its backups. Export any data you need before destroying.

Prerequisites

Local tools

Install these on the workstation that will run the installer. The preflight step in voipbin-install init verifies each version.

Tool

Min. version

Notes

gcloud CLI

400.0.0

Both gcloud auth login and gcloud auth application-default login required.

terraform

1.5.0

Bundled under terraform/ in the installer repo.

ansible

2.15.0

Install via pip install ansible.

kubectl

1.28.0

Used by the installer and for verification.

python3

3.10.0

Required by the installer CLI.

sops

3.7.0

Encrypts secrets.yaml with GCP KMS.

Python dependencies are installed with pip install -r requirements.txt after cloning the installer repo.

GCP account

The installer refuses to proceed unless the following are true:

  • A GCP project exists with billing enabled.

  • The authenticated principal has Owner or Editor on the project, or the least-privilege set of twelve roles in config/gcp_iam_roles.yaml (Compute Admin, Container Admin, Cloud SQL Admin, DNS Admin, Cloud KMS Admin, Secret Manager Admin, Service Account Admin, Service Account User, Project IAM Admin, Storage Admin, Service Usage Admin, IAP-Secured Tunnel User). In addition, the principal running the installer needs roles/compute.osLogin and roles/compute.osAdminLogin to SSH into the Kamailio and RTPEngine VMs via OS Login during the Ansible stage.

  • You own a domain name. The installer uses api.<domain>, hook.<domain>, admin.<domain>, talk.<domain>, meet.<domain>, and sip.<domain>.

GCP quotas

The init command checks regional quotas. New GCP projects ship with 8 vCPUs and 8 in-use external IPs, which is below what VoIPBin needs. Request increases for the following before installing if you do not want the apply to fail mid stage:

Quota

Minimum required

Notes

CPUs (region)

12

2x GKE nodes + 2x VoIP VMs

In-use external IPs

10

NAT, LBs, RTPEngine static IPs

Static external IPs

4

usually sufficient by default

SSD total (GB)

100

usually sufficient by default

Quota increases are requested at https://console.cloud.google.com/iam-admin/quotas.

GCP APIs

The init command automatically enables sixteen APIs on the project: compute, container, sqladmin, dns, cloudkms, secretmanager, cloudresourcemanager, iam, servicenetworking, storage, storage-api, logging, monitoring, oslogin, serviceusage, and iap.

Two authentications, not one

VoIPBin’s installer depends on both of the following being live, and this catches operators by surprise often enough to be worth calling out:

  1. gcloud auth login for the human/CLI principal.

  2. gcloud auth application-default login for Terraform and SOPS, which read Application Default Credentials.

The preflight in init checks both and offers to refresh ADC if it is missing or expired.

Install

The happy path is three commands. They are idempotent and resumable.

git clone https://github.com/voipbin/install.git
cd install
pip install -r requirements.txt

./voipbin-install init      # interactive wizard + GCP bootstrap
./voipbin-install apply     # provision and deploy
./voipbin-install verify    # health check

init (interactive wizard)

The init command runs a multi-step bootstrap, in order:

  1. Preflight checks for the six local tools.

  2. gcloud user auth and Application Default Credentials.

  3. Eight wizard questions: GCP project ID, region, GKE cluster type (zonal or regional), TLS strategy (self-signed or byoc), Docker image tag strategy (latest or pinned via config/versions.yaml), base domain name, Kamailio TLS cert mode (self_signed or manual), and Cloud DNS mode (auto or manual).

  4. Project existence and billing.

  5. Quota check against config/gcp_quotas.yaml.

  6. Enable sixteen GCP APIs.

  7. Create the voipbin-installer service account with the IAM role bindings defined in config/gcp_iam_roles.yaml.

  8. Create a KMS key ring and crypto key.

  9. Generate six secrets (jwt_key, cloudsql_password, redis_password, rabbitmq_user, rabbitmq_password, api_signing_key) and encrypt them into secrets.yaml with SOPS bound to the KMS key.

  10. Write .sops.yaml.

  11. Save config.yaml.

  12. Print a cost summary.

Wizard questions

The eight questions and their accepted values:

Question

Options / Example

GCP project ID

my-voipbin-project

Region

us-central1 (or any GCP region)

GKE cluster type

zonal (cheaper) or regional (HA)

TLS strategy

self-signed or byoc

Docker image tag strategy

latest or pinned

Domain name

voipbin.example.com

Kamailio cert mode

self_signed (auto) or manual

Cloud DNS mode

auto (GCP manages DNS) or manual

Useful flags:

./voipbin-install init --reconfigure          # re-run the wizard
./voipbin-install init --config path/to.yaml  # import existing config
./voipbin-install init --skip-api-enable      # APIs already enabled
./voipbin-install init --skip-quota-check     # bypass quota gate
./voipbin-install init --dry-run              # show what would happen

The two files written are everything you need to reproduce the install:

  • config.yaml: non-sensitive. Safe to commit if you want a record per environment.

  • secrets.yaml: SOPS-encrypted with GCP KMS. Decrypt locally with sops --decrypt secrets.yaml.

Warning

Back up secrets.yaml and the KMS key ring. If the key ring is destroyed or the GCP project is deleted, secrets.yaml becomes unrecoverable.

apply (eight-stage deploy)

./voipbin-install apply executes eight pipeline stages in order. The pipeline checkpoints between stages, so if a run fails you can fix the problem and rerun the same command; it picks up from the failed stage.

Stage

What it does

Typical duration

terraform_init

Initialize Terraform backend (GCS state bucket)

1 to 2 minutes

reconcile_imports

Import drifted GCP resources into Terraform state

1 to 3 minutes

terraform_apply

Provision VPC, GKE, Cloud SQL, VMs

12 to 18 minutes

reconcile_outputs

Read Terraform outputs into config.yaml

under 1 minute

k8s_apply

Deploy Kubernetes workloads to GKE

3 to 5 minutes

reconcile_k8s_outputs

Read Kubernetes LB IPs into config.yaml

under 1 minute

cert_provision

Issue Kamailio TLS certificates

1 to 2 minutes

ansible_run

Configure Kamailio and RTPEngine VMs

5 to 8 minutes

Useful flags:

./voipbin-install apply --dry-run               # plan, do not change
./voipbin-install apply --auto-approve          # skip prompts
./voipbin-install apply --stage terraform_init  # only run this stage
./voipbin-install apply --stage k8s_apply
./voipbin-install apply --stage ansible_run

verify (health check)

The verify command runs a series of checks against the live deployment and prints a pass, warn, or fail per check. The current set of checks covers, in order: GKE cluster status, pod readiness in three namespaces, service endpoint availability in three namespaces, Kamailio and RTPEngine VM run state, Cloud SQL instance state, DNS resolution for api.<domain>, HTTPS reachability of https://api.<domain>/health, and a TCP socket probe of SIP port 5060. Each check prints the underlying command on failure so you can rerun individual probes manually.

Note

The SIP probe uses a TCP socket connect. If you need to verify UDP reachability for media or signalling, use a separate tool such as nc -zuv and the GCP firewall log stream.

status (deployment overview)

The status command shows a summary of the current deployment state:

./voipbin-install status            # human-readable output
./voipbin-install status --json     # machine-readable JSON

cert (certificate management)

The cert subcommand manages Kamailio TLS certificates:

./voipbin-install cert status       # show per-SAN cert expiry and mode
./voipbin-install cert renew        # re-run cert_provision stage
./voipbin-install cert renew --force  # force re-issuance even if not expired
./voipbin-install cert export-ca --out ca.pem  # export self-signed CA

DNS step

After apply, the installer prints the DNS records you must publish if you chose dns_mode: manual, or the four nameservers to delegate to if you chose dns_mode: auto. Required records, with example.com standing in for your domain:

Subdomain

Type

Value

api.example.com

A

External LB IP

hook.example.com

A

External LB IP

admin.example.com

A

External LB IP

talk.example.com

A

External LB IP

meet.example.com

A

External LB IP

sip.example.com

A

Kamailio external LB IP

Note

The hook subdomain is commonly missed. It is required for webhook ingress (HTTP and HTTPS callbacks from external providers).

DNS propagation can take up to 48 hours for NS delegation changes. Once records resolve, rerun ./voipbin-install verify.

Configuration files

The installer writes two files in the working directory.

config.yaml (non-sensitive)

Holds every parameter from the wizard and the defaults applied around them. Environment variables prefixed with VOIPBIN_ override a value at runtime, for a single CLI invocation (for example VOIPBIN_REGION=europe-west1 ./voipbin-install apply).

Typical contents:

gcp_project_id: my-project-123
region: us-central1
zone: us-central1-a
gke_type: zonal
tls_strategy: self-signed
image_tag_strategy: pinned
domain: voipbin.example.com
dns_mode: auto
gke_machine_type: n1-standard-2
gke_node_count: 2
vm_machine_type: f1-micro
kamailio_count: 1
rtpengine_count: 1

The schema is enforced by config/schema.py. To change a value after the initial install, edit config.yaml and rerun ./voipbin-install apply.

secrets.yaml (SOPS-encrypted)

Holds the six secrets generated by init, encrypted with GCP KMS via SOPS:

  • jwt_key: JWT signing key consumed by the API and webhook layers.

  • cloudsql_password: Cloud SQL root password. The instance is created with this value.

  • redis_password: Redis AUTH password.

  • rabbitmq_user and rabbitmq_password. RabbitMQ credentials.

  • api_signing_key: request signing key used between internal services.

Editing a secret:

sops secrets.yaml         # opens editor with decrypted view
# edit the field, save
./voipbin-install apply   # re-render ConfigMap, Secret, and VM .env

After editing, the Kubernetes ConfigMap, the Kubernetes Secret, and the Kamailio VM .env are re-rendered consistently in a single apply.

Where each value lands

Most of the wizard’s answers and secrets fan out into multiple targets at apply time. The most important paths to know:

Source

Target type

Where it ends up

config.yaml

Terraform variables

terraform/ tfvars and resource arguments

config.yaml

Kubernetes ConfigMap

voipbin-config in bin-manager and infrastructure

config.yaml

Ansible group_vars

Kamailio VM /opt/kamailio-docker/.env

secrets.yaml

Kubernetes Secret

voipbin-secret in bin-manager

secrets.yaml

Kubernetes Secret (composite)

RABBITMQ_ADDRESS and REDIS_ADDRESS in voip

secrets.yaml

Ansible group_vars

Kamailio VM .env (RABBITMQ_URL, etc.)

Environment variables and post-install configuration

The defaults produce a deployment that boots and passes verify, but production workloads need adjustments. This section enumerates what to touch and where.

2. Generated credentials (RabbitMQ, Redis, MySQL, JWT, API signing)

These live in the SOPS-encrypted secrets.yaml and are wired into the ConfigMap, Secret, and VM .env automatically. Rotate them through SOPS:

sops secrets.yaml
./voipbin-install apply

3. SIP and PSTN settings

These are not part of the wizard. Edit them in the appropriate place and rerun the relevant stage.

PSTN allowlist

Kamailio only accepts inbound PSTN traffic from explicitly whitelisted source IPs. Populate the list with VOIPBIN_PSTN_WHITELIST_IPS and rerun the Ansible stage:

export VOIPBIN_PSTN_WHITELIST_IPS="203.0.113.10,198.51.100.4"
./voipbin-install apply --stage ansible_run

The value flows through ansible/group_vars/all.yml into the Kamailio env template as PSTN_WHITELIST_IPS.

SIP auth backend

The four KAMAILIO_AUTH_* variables in the Kamailio env template default to empty. Production deployments point them at a database that holds SIP credentials:

  • KAMAILIO_AUTH_DB_URL: connection string Kamailio uses for the auth database.

  • KAMAILIO_AUTH_USER_COLUMN: defaults to username.

  • KAMAILIO_AUTH_DOMAIN_COLUMN: defaults to realm.

  • KAMAILIO_AUTH_PASSWORD_COLUMN: defaults to password.

Set them via Ansible extra vars or by editing ansible/group_vars/kamailio.yml and rerunning ./voipbin-install apply --stage ansible_run.

RTPEngine port range

The default media port range is 20000-65535. If your network only opens 20000-30000, set rtpengine_port_max: 30000 in ansible/group_vars/rtpengine.yml and rerun the Ansible stage. Make sure the firewall rule matches.

4. TLS certificate strategy

The tls_strategy chosen in the wizard governs how the frontend TLS certificate is managed. There are two valid values:

  • self-signed (default): on first apply, the installer generates a self-signed RSA-2048 certificate and stores it in the voipbin-tls Kubernetes Secret in both bin-manager and square-manager namespaces, and as base64-encoded env vars in voipbin-secret. Browsers will show a certificate warning until you replace it with a CA-issued cert. Use only for initial bring-up or internal testing.

  • byoc (Bring Your Own Cert): the operator pre-creates the voipbin-tls Secret with a real CA-issued certificate before the k8s_apply stage. The installer detects populated SSL keys and skips its own writes. To use BYOC mode, create the secret in both namespaces before running apply:

    kubectl -n bin-manager create secret tls voipbin-tls \
      --cert=/path/to/fullchain.pem --key=/path/to/privkey.pem
    kubectl -n square-manager create secret tls voipbin-tls \
      --cert=/path/to/fullchain.pem --key=/path/to/privkey.pem
    

    Then run:

    ./voipbin-install apply --stage k8s_apply
    

To change strategy after install, edit tls_strategy in config.yaml and rerun apply.

5. Third-party integrations

VoIPBin services can integrate with external providers for LLM, ASR, TTS, email, SMS, and payments. The installer ships with these slots intentionally empty, because they are operator-specific and not free. Add them as Kubernetes Secrets in the bin-manager namespace, then mount them into the relevant deployments.

Provider categories you will likely need to wire up:

  • An LLM or AI provider.

  • A speech-to-text provider.

  • A text-to-speech provider.

  • An email API or SMTP provider.

  • An SMS provider.

  • A payment provider for billing flows.

Pick providers based on your own region, compliance, and pricing constraints. Until the relevant keys are wired up, the corresponding flow actions return provider not configured errors; the platform itself stays healthy and the rest of the channels remain usable.

Recommended pattern: a separate Secret per provider so rotation is independent.

kubectl -n bin-manager create secret generic voipbin-llm \
  --from-literal=LLM_API_KEY=...

Then patch the deployment to mount it via envFrom. Track the provider matrix per service in your own runbook; upstream manifests deliberately do not enumerate provider keys because the supported list evolves.

6. Scaling

The defaults are minimal:

  • GKE: 2 nodes of n1-standard-2.

  • Kamailio: 1 VM of f1-micro.

  • RTPEngine: 1 VM of f1-micro.

  • Backend deployments: 1 replica each, 50m CPU and 64Mi memory request, 200m and 256Mi limits.

For anything beyond a demo, raise the VM types and replica counts. Adjust gke_machine_type, gke_node_count, vm_machine_type, kamailio_count, and rtpengine_count in config.yaml and rerun ./voipbin-install apply.

Backend replica counts live in the per-service manifest under k8s/backend/services/<name>.yaml. For now, edit those files directly; a future installer revision will surface scaling profiles through config.yaml.

Consolidated environment variable map

Every variable a fresh install touches comes from one of three sources: the wizard, generated secrets, or operator overrides.

Wizard-sourced (config.yaml)

  • gcp_project_id

  • region, zone

  • gke_type (zonal or regional)

  • tls_strategy (self-signed or byoc)

  • image_tag_strategy (latest or pinned)

  • domain

  • dns_mode (auto or manual)

  • gke_machine_type, gke_node_count

  • vm_machine_type, kamailio_count, rtpengine_count

Any of these can be overridden at runtime with the VOIPBIN_ prefix (uppercased), for example VOIPBIN_REGION=europe-west1.

Generated and SOPS-encrypted (secrets.yaml)

  • jwt_key

  • cloudsql_password

  • redis_password

  • rabbitmq_user, rabbitmq_password

  • api_signing_key

Ansible variables (Kamailio VMs)

Defined in ansible/group_vars/all.yml and ansible/group_vars/kamailio.yml. The ones operators routinely change:

  • domain via VOIPBIN_DOMAIN or config.yaml.

  • image_tag via VOIPBIN_IMAGE_TAG.

  • pstn_whitelist_ips via VOIPBIN_PSTN_WHITELIST_IPS.

  • kamailio_auth_db_url and the three kamailio_auth_*_column variables via extra-vars or group_vars/kamailio.yml.

  • kamailio_shm_size, kamailio_pkg_size: memory tuning.

  • pike_enabled, pike_rate, pike_timeout: anti-flood.

  • homer_enabled, homer_uri: HEP capture.

Kubernetes ConfigMap (voipbin-config)

In namespaces bin-manager and infrastructure. Placeholders are substituted at apply time:

  • DOMAIN, DB_HOST, DB_PORT, DB_NAME, CLOUDSQL_CONNECTION_NAME

  • REDIS_URL, RABBITMQ_URL, CLICKHOUSE_URL

Kubernetes Secret (voipbin-secret)

In namespace bin-manager:

  • JWT_KEY, DB_USER, DB_PASSWORD, REDIS_PASSWORD, RABBITMQ_PASSWORD, API_SIGNING_KEY

Operations and troubleshooting

Day-to-day commands

# Show the current pipeline state
./voipbin-install status

# Show status as JSON (for scripting)
./voipbin-install status --json

# Verify deployment health (API, SIP, pods, TLS)
./voipbin-install verify

# Run only a specific health check
./voipbin-install verify --check http_health

# Re-render and apply just the K8s manifests after editing a value
./voipbin-install apply --stage k8s_apply

# Re-render the Kamailio VM .env after editing group_vars
./voipbin-install apply --stage ansible_run

# Show Kamailio TLS certificate status
./voipbin-install cert status

# Renew Kamailio TLS certificates
./voipbin-install cert renew

# Show help for any command
./voipbin-install --help
./voipbin-install apply --help

# Inspect what secrets are stored
sops --decrypt secrets.yaml

# SSH into Kamailio for debugging
gcloud compute ssh kamailio-0 --tunnel-through-iap \
  --project "$(yq .gcp_project_id config.yaml)"

# Get logs from a backend service
kubectl -n bin-manager logs deploy/call-manager --tail 200

# Tear everything down (irreversible, including the database)
./voipbin-install destroy

Common issues

Terraform: quota exceeded

Error: googleapi: Error 403: Quota exceeded. Request quota increases at https://console.cloud.google.com/iam-admin/quotas, then rerun ./voipbin-install apply. Common quotas: CPUS_ALL_REGIONS, IN_USE_ADDRESSES, SSD_TOTAL_GB.

Terraform: state lock

Error: Error acquiring the state lock. Only when you are certain no other process is running Terraform:

cd terraform
terraform force-unlock LOCK_ID

Terraform: API not enabled

Error: googleapi: Error 403: ... API not enabled. Re-run ./voipbin-install init to enable APIs, or enable manually:

gcloud services enable container.googleapis.com --project PROJECT_ID

Terraform: state bucket missing

Error: Failed to get existing workspaces: storage: bucket doesn't exist. The GCS state bucket must exist before terraform init. The apply command creates it automatically; if you are running Terraform manually, create it first:

gsutil mb -p PROJECT_ID gs://PROJECT_ID-voipbin-tf-state

Terraform: permission denied

Error: googleapi: Error 403: Required permission. Re-check the authenticated principal:

gcloud auth list
gcloud projects get-iam-policy PROJECT_ID

The minimum role set is in config/gcp_iam_roles.yaml (12 roles). The principal also needs roles/compute.osLogin and roles/compute.osAdminLogin for Ansible VM access.

Ansible: IAP tunnel connection failed

ERROR! Timeout waiting for connection or Permission denied (publickey).

  1. Verify IAP SSH works directly: gcloud compute ssh VM_NAME --tunnel-through-iap --project PROJECT_ID --zone ZONE.

  2. Ensure the IAP API is enabled.

  3. Check the IAP firewall rule exists.

  4. Verify the principal has roles/iap.tunnelResourceAccessor.

Ansible: docker install failed on a fresh VM

Could not get lock /var/lib/dpkg/lock typically means cloud-init is still running. Wait for it and rerun the stage:

gcloud compute ssh kamailio-0 --tunnel-through-iap \
  -- 'cloud-init status --wait'
./voipbin-install apply --stage ansible

Kubernetes: pod in CrashLoopBackOff

kubectl get pods -n bin-manager
kubectl logs POD_NAME -n bin-manager
kubectl describe pod POD_NAME -n bin-manager

Most common causes:

  • Cloud SQL Proxy not ready (check infrastructure namespace first).

  • Redis or RabbitMQ not ready.

  • Missing ConfigMap or Secret values.

Kubernetes: ImagePullBackOff

  1. Verify the image exists at the tag declared in config/versions.yaml.

  2. GKE nodes need internet access through Cloud NAT.

  3. If images live in a private GAR or GCR repository, confirm the GKE node service account has roles/artifactregistry.reader (GAR) or roles/storage.objectViewer on the registry bucket (legacy GCR).

Cloud SQL: connection refused from pods

kubectl get pods -n infrastructure -l app=cloudsql-proxy
kubectl logs -n infrastructure -l app=cloudsql-proxy

Verify the Cloud SQL instance is up, the proxy service account has permissions, and the connection name matches the Terraform output.

DNS: domain not resolving

When dns_mode: auto:

gcloud dns managed-zones describe voipbin-zone --project PROJECT_ID \
  --format="value(nameServers)"

Update your registrar’s NS records to point at those nameservers. NS delegation can take up to 48 hours.

When dns_mode: manual: pull the external LB IP from Terraform outputs and create A records for api, hook, admin, talk, meet, and sip subdomains at your DNS provider.

SIP: devices cannot register

  1. Confirm Kamailio is running on the VM: docker compose ps via IAP SSH.

  2. Inspect the logs: docker compose logs --tail 100.

  3. List the Kamailio load balancer resources and check health, since the exact resource names vary by deployment environment:

    gcloud compute target-pools list --filter="name~kamailio"
    gcloud compute forwarding-rules list --filter="name~kamailio"
    

Audio: calls connect but no audio

  1. Confirm RTPEngine is running on the VM.

  2. Check the firewall allows the RTP UDP port range.

  3. Verify RTPEngine sees the correct external IP, especially after moving regions.

Rollback

Kubernetes rollout:

kubectl rollout undo deployment/DEPLOYMENT_NAME -n NAMESPACE

Terraform state from a previous version:

gsutil ls -la gs://PROJECT_ID-voipbin-tf-state/default.tfstate
gsutil cp gs://PROJECT_ID-voipbin-tf-state/default.tfstate#VERSION terraform.tfstate
cd terraform && terraform apply

Full teardown and rebuild:

./voipbin-install destroy
./voipbin-install apply

Where to go next

  • The installer repo’s own docs/troubleshooting.md has a wider list of error symptoms by stage.

  • The installer’s README.md lists exact IAM roles and Terraform outputs.

  • For platform support, contact support@voipbin.net.