Deploy Qrvey v9.4
The following sections show how to deploy Qrvey v9.4. To reconfigure a Terraform-managed Qrvey cluster to use ArgoCD management, you can use a single deploy command to build and adopt the infrastructure. You can also apply a two-step approach (apply then adopt full) to each cloud.
The following information addresses the primary usage of these scripts with Docker.
Before You Begin
You need the following items in place before you can use these scripts with Docker.
- Running Docker Desktop container.
- Access to the
qrveydev.azurecr.ioregistry. - A
config.json(AWS) orconfig.yaml(Azure) file containing the required fields.
Configuration
Your config.json file should include the following fields as part of its structure. The following example presents a sample AWS configuration:
{
"account_config": {
"access_key_id": "AKIA...",
"secret_access_key": "...",
"region": "us-east-1",
"bucket": "qrvey-deploy-aws-terraform-states-...",
"key": "mp/deploy-aws/.../customer.tfstate"
},
"variables": {
"qrvey_chart_version": "<QRVEY_VERSION>",
"GITOPS_ADMIN_TOKEN": "ATATT3xFfGF0...",
"GITOPS_ADMIN_EMAIL": "user@company.com",
"customer_info": {
"email": "user@company.com",
"company": "customername"
},
"customer_repo": {
"auto_create_repo": true,
"provider": "bitbucket",
"org": "qrvey",
"name": "<deployment_id>-platform-control",
"deploy_key": ""
},
"registry_user": "qrveydev",
"registry_key": "..."
}
}
An Azure configuration uses the same variables block and uses the Azure shape (ARM credentials and Blob back end) in the account_config block:
{
"account_config": {
"ARM_CLIENT_ID": "...", "ARM_CLIENT_SECRET": "...",
"ARM_TENANT_ID": "...", "ARM_SUBSCRIPTION_ID": "...",
"resource_group_name": "qrvey-<id>-rg",
"storage_account_name": "qrveytf<id>",
"container_name": "tfstate", "key": "<id>.tfstate"
}
}
The following sections provide additional details for these fields.
account_config
This reference includes a bucket and key pointing to the S3 back end where the customer's Terraform state is stored. For Azure, the storage_account_name, container_name, and key point to the Blob back end.
qrvey_chart_version
This entry lists the OCI chart version for the main Qrvey application chart, and must match a tag actually published to the OCI registry. Contact the DevOps team to obtain the current value.
customer_repo
This entry defines the Git repository that holds the platform-control configuration.
| Parameter | Settings |
|---|---|
auto_create_repo | Set to true to trigger scripts to automatically create the repository. |
provider | Set to bitbucket, github, or gitlab. |
org | Add the organization or workspace that owns the repository. |
name | Add the repository name (convention: <deployment_id>-platform-control). |
deploy_key | Leave this parameter empty. This is automatically populated by setup-repo. |
GITOPS_ADMIN_TOKEN
This entry stores the API token used for Git operations (push, deploy key upload, repository creation).
| Provider | Token Type | Minimum Permissions |
|---|---|---|
| Bitbucket | API Token (create here) | read:repository:bitbucket, write:repository:bitbucket, admin:repository:bitbucket, write:ssh-key:bitbucket |
| GitHub | Classic PAT | repo scope. |
| GitLab | Personal Access Token | api scope. |
GITOPS_ADMIN_EMAIL
This required entry stores the Git/Atlassian account email that owns GITOPS_ADMIN_TOKEN. Bitbucket uses the Basic-auth username for API calls and HTTPS pushes (GITOPS_ADMIN_EMAIL:GITOPS_ADMIN_TOKEN). Both must belong to the same account, or operations fail with a 403 Forbidden error.
The read:webhook:bitbucket and write:webhook:bitbucket webhook scopes are required because the setup repository registers an ArgoCD push-notification webhook on the platform-control repository at https://argocd.<deployment>.<dns_zone>/api/webhook. With the webhook in place, each Git push refreshes all affected applications within 25 seconds. Without it, ArgoCD reverts to a 3-minute polling cycle, which makes adopt upgrade-version and other ArgoCD workflows appear stalled because of the polling gap. Adoption proceeds without the webhook scopes, but the operator experience is affected, and the workflow requires a follow-up token rotation with the correct scopes.
registry_user / registry_key
This entry stores the OCI registry credentials used for Docker login and by Terraform to pull Helm charts.
Registry login:
jq -r '.variables.registry_key' config.json | \
docker login qrveydev.azurecr.io -u "$(jq -r '.variables.registry_user' config.json)" --password-stdin
Domain and Certificate Configuration
The dns_zone_name sets the hostname where the platform serves. Qrvey supports the following use cases:
Qrvey mp.qrveyapp.com Zone
Automatically returns <deployment_id>.mp.qrveyapp.com, which is applicable for most users. Qrvey creates the DNS record and the HTTPS certificate, automatically writing to the mp.qrveyapp.com zone.
Single Manual DNS Record
When using any other domain, (such as qrvey.me or a customer-owned domain), use a manual record entry:
"dns_zone_name": "analytics.acme.com"
Qrvey cannot write to that zone. During adoption, create the following records at your DNS provider:
analytics.acme.com > <load-balancer> (CNAME for a hostname)
argocd.analytics.acme.com > <load-balancer> (CNAME for a hostname)
The HTTPS certificate issues automatically when these records are resolved.
Installation Options
Qrvey offers the following installation options.
Option A: Unified Image (Recommended)
The unified image bundles the AWS and Azure Terraform trees, adoption and recovery scripts, Copier templates, and CLIs, and automatically detects the target cloud from your configuration. Its deploy verb runs terraform apply and adopt full in the same process. The tag applies to multiple architectures (linux/amd64 and linux/arm64), so Docker uses the architecture matching your machine automatically.
After you have set up your config.json file and registry login, run the following commands:
touch terraform-output.yaml
If missing during the bind mount, Docker creates terraform-output.yaml as a directory (not a file).
mkdir work
IMAGE=qrveydev.azurecr.io/qrvey-terraform:<ask_the_devops_team>
docker run --rm \
-v $(pwd)/config.json:/app/config.json \
-v $(pwd)/terraform-output.yaml:/app/aws/terraform-output.yaml \
-v $(pwd)/terraform-output.yaml:/app/azure/terraform-output.yaml \
-v $(pwd)/work:/app/work \
$IMAGE deploy
IMAGE=— Ask the DevOps team for the current tag (tags roll regularly).terraform-output.yaml- Mounted at both/app/aws/...and/app/azure/..., the unified image automatically detects the cloud and uses that cloud's tree for the file. Mounting both means the active tree always reads and writes it, sodeploywrites the generatedterraform-output.yamlback to your host file regardless of cloud (the inactive mount is harmless). Theconfig.jsonis cloud-agnostic at/app/config.json.deploy- Runsterraform applyandadopt fullat the same time. An apply failure aborts the process beforeadoptcan run.
Note: The first phase of
adopt full(setup-repo) generates an SSH deploy key and writes to the bind-mountedconfig.json(undercustomer_repo.deploy_key) for use by later phases.
The deploy process provisions the infrastructure and completes the ArgoCD adoption.
Option B: One Image to Each Cloud
This approach deploys one image for each cloud — the image you pick is the cloud (no automatic detection). The scripts run as separate steps instead of a single deploy. You can use this option when you want to inspect the apply before adopting, or when adopting an existing cluster. The following images are available for your cloud:
AWS_IMAGE=qrveydev.azurecr.io/qrvey-terraform-aws:<QRVEY_VERSION>
AZURE_IMAGE=qrveydev.azurecr.io/qrvey-terraform-azure:<QRVEY_VERSION>
-
Run the Terraform
apply. Use the same command for a new account or when adopting an existing pre-ArgoCD cluster:docker run --rm \
-v $(pwd)/config.json:/app/qrvey/config.json \
-v $(pwd)/terraform-output.yaml:/app/qrvey/terraform-output.yaml \
-v $(pwd)/work:/app/work \
$AWS_IMAGE apply # Azure: $AZURE_IMAGE applyThis command performs the following tasks:
- Builds or updates the complete stack: VPC, EKS, RDS, IAM, Route53 + ACM, ArgoCD bootstrap, OCI registry secret, AppProjects.
- Writes the
terraform-output.yaml.
For a fresh account, the
applybuilds everything from scratch. For an existing cluster, it creates only the missing pieces (ArgoCD namespace/Helm release, AppProjects, OCI registry secret, and the load balancer-controller and Route53 IAM) and applies any pending state migrations.A full
applyis the only supported path. Avoid using a targeted (-target)apply, which cannot process the Terraform moved blocks used by an upgrade. Thisapplyfails with moved resource instances excluded by targeting and silently skips dependencies used by the gateway and routes. -
Run adoption (full pipeline). The adoption scripts write intermediate artifacts (generated repository, copier answers, kong values) to
/app/work/.Mount a local
work/directory so the artifacts persist if the pipeline fails and needs to resume. Use the same image for each cloud.mkdir -p work
# Common mount flags (array works in both bash and zsh)
MOUNT=(-v $(pwd)/config.json:/app/qrvey/config.json -v $(pwd)/terraform-output.yaml:/app/qrvey/terraform-output.yaml -v $(pwd)/work:/app/work)
docker run --rm "${MOUNT[@]}" $AWS_IMAGE adopt full # Azure: $AZURE_IMAGE adopt full
The adopt full command runs the following processes:
- Git repository setup.
- Copier render of the
platform-controlrepository. - Live cluster value extraction.
- Push to Git.
- ArgoCD bootstrap
- Kong rip-and-replace (with DNS cutover and ACME wait).
- Monitoring rip-and-replace.
- Legacy Helm release secret cleanup.
Upgrade to Qrvey v9.4
To upgrade your Qrvey instance to v9.4, make the following changes to your existing config.json file:
-
Update the
qrvey_chart_versionvariable in yourconfig.jsonfile to the new version. -
Add
GITOPS_ADMIN_TOKENandGITOPS_ADMIN_EMAILentries to yourvariablessection. -
Add a customer repository section:
"customer_repo": {
"auto_create_repo": true,
"provider": "bitbucket",
"org": "qrvey",
"name": "deploytestqa405-platform-control",
"deploy_key": ""
}
After updating these entries, complete the installation steps using the updated qrvey_version value in the relevant commands.
This applies the upgrade and updates your environment to the specified version.
Delete a Qrvey Instance
To remove (destroy) a Qrvey instance and all associated resources, follow these steps:
-
Navigate to the directory containing your
config.jsonfile. -
Run the
destroycommand to preview the resources that will be removed (similar to a Terraform "plan"). For example:docker run --platform=linux/amd64 -v $(pwd)/config.json:/app/qrvey/config.json -it --rm qrvey.azurecr.io/qrvey-terraform:${qrvey_version} destroy -
To remove (destroy) the instance and all associated resources, run the following command:
docker run --platform=linux/amd64 -v $(pwd)/config.json:/app/qrvey/config.json -v $(pwd)/terraform-output.yaml:/app/aws/terraform-output.yaml -v $(pwd)/terraform-output.yaml:/app/azure/terraform-output.yaml -v $(pwd)/work:/app/work -it --rm qrveydev.azurecr.io/qrvey-terraform:9.4-20260714-3306 destroy
Warning: When the resources are removed, all data and metadata associated with the instance is permanently deleted and cannot be recovered.