Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tensor9.com/llms.txt

Use this file to discover all available pages before exploring further.

This guide shows common workflows for using the tensor9 CLI to manage your apps, stacks, and appliances.

Prerequisites

Before starting, ensure you have:
  • Installed the tensor9 CLI
  • Set your API key: export T9_API_KEY=<your-key>
  • Configured AWS credentials for your Tensor9 AWS account

Initial setup workflow

Set up Tensor9 in your AWS account for the first time.
1

Set up your control plane

Run the interactive setup:
tensor9 vendor setup
Or provide all parameters:
tensor9 vendor setup \
  -cloud aws \
  -region us-west-2 \
  -awsProfile my-tensor9-profile
This creates your Tensor9 control plane in your AWS account. It takes several minutes to complete.
2

Verify setup

Check that your control plane is ready:
tensor9 report
You should see your vendor information displayed.
3

Create your first app

tensor9 app create \
  -name my-app \
  -displayName "My Application"
Your app is now created and ready to have a stack bound to it.

Publish and bind your origin stack

Publish an origin stack and bind it to your app.

For Terraform stacks

1

Publish your Terraform stack

From your Terraform workspace directory:
tensor9 stack publish \
  -stackType TerraformWorkspace \
  -stackS3Key my-app-stack \
  -dir .
This uploads your Terraform files and returns a native stack ID like:
s3://t9-ctrl-000001/terraform-stacks/origins/my-app-stack.tf.tgz
2

Bind the stack to your app

tensor9 stack bind \
  -appName my-app \
  -stackType TerraformWorkspace \
  -nativeStackId "s3://t9-ctrl-000001/terraform-stacks/origins/my-app-stack.tf.tgz"
Your stack is now bound to your app.

For Docker container stacks

1

Push your container image to ECR

First, authenticate to your Tensor9 AWS account’s ECR:
aws ecr get-login-password --region us-west-2 --profile my-tensor9-profile | \
  docker login --username AWS --password-stdin <account-id>.dkr.ecr.us-west-2.amazonaws.com
Tag and push your image:
docker tag my-app:latest <account-id>.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
docker push <account-id>.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
The native stack ID is the image URI:
<account-id>.dkr.ecr.us-west-2.amazonaws.com/my-app:latest
2

Bind the container to your app

tensor9 stack bind \
  -appName my-app \
  -stackType DockerContainer \
  -nativeStackId "<account-id>.dkr.ecr.us-west-2.amazonaws.com/my-app:latest"
Your container is now bound to your app.

For Docker Compose stacks

Before publishing your Docker Compose file, ensure all container images referenced in your compose file are pushed to their registries (ECR, Docker Hub, etc.). These images must be available when you create a release.
1

Publish your Docker Compose file

tensor9 stack publish \
  -stackType DockerCompose \
  -stackS3Key my-app-compose \
  -file docker-compose.yml
Returns a native stack ID like:
s3://t9-ctrl-000001/my-app-compose.yml
2

Bind the stack to your app

tensor9 stack bind \
  -appName my-app \
  -stackType DockerCompose \
  -nativeStackId "s3://t9-ctrl-000001/my-app-compose.yml"

For CloudFormation stacks

1

Deploy your CloudFormation stack

Deploy your CloudFormation stack using the AWS CLI:
aws cloudformation create-stack \
  --stack-name my-app-stack \
  --template-body file://template.yaml \
  --region us-west-2 \
  --profile my-tensor9-profile
2

Get the stack ARN

aws cloudformation describe-stacks \
  --stack-name my-app-stack \
  --region us-west-2 \
  --query 'Stacks[0].StackId' \
  --output text
This returns an ARN like:
arn:aws:cloudformation:us-west-2:123456789012:stack/my-app-stack/a1b2c3d4
3

Bind the CloudFormation stack

tensor9 stack bind \
  -appName my-app \
  -stackType CloudFormation \
  -nativeStackId "arn:aws:cloudformation:us-west-2:123456789012:stack/my-app-stack/a1b2c3d4"

Create a test appliance

Create a test appliance for testing your releases before deploying to customers.
1

Create a test appliance

tensor9 test appliance create \
  -appName my-app \
  -name my-test-appliance
The test appliance will be created asynchronously.
2

Monitor appliance creation

Check the status periodically:
tensor9 report
Wait until the test appliance status shows “Live”. This typically takes 10-15 minutes.

Deploy to a test appliance

Deploy your stack to a test appliance to verify it works before releasing to customers.
1

Create a release

Create a release for your test appliance:
tensor9 stack release create \
  -appName my-app \
  -testApplianceName my-test-appliance \
  -vendorVersion "1.0.0" \
  -description "Initial release" \
  -notes "First production release"
After a few minutes, the deployment stack downloads to a directory named my-test-appliance, alongside a companion my-test-appliance.audit directory containing the audit stack for pre-deployment review.
2

Review the audit stack (optional)

Before deploying, you can scan the audit stack with your standard IaC security and policy tooling. The audit stack is a Tensor9-free copy of the deployment stack - same application infrastructure, without any Tensor9 providers or runtime plumbing - so scanners can plan and inspect it standalone:
cd my-test-appliance.audit
tofu init
tofu plan

# Security / policy scans
tfsec .
checkov -d .
The audit stack is for review only. Do not apply it. Only the deployment stack (in my-test-appliance) provisions a working install.
3

Deploy the release

cd my-test-appliance
tofu init
tofu apply
Your application is now deployed in the test appliance.
4

Verify the deployment

Check the deployed resources:
kubectl get deployments
kubectl get services
kubectl get pods
For services with external ports, get the load balancer endpoint:
kubectl get service <service-name> -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
Access your application through the load balancer URL.

Create a customer appliance

Enable your customer to create an appliance for your app in their environment.
1

Generate signup link

Generate a signup link for your customer:
tensor9 app signup-link -appName my-app
Send the generated URL to your customer. They’ll use it to create their appliance.
2

Customer creates appliance

The customer uses the signup link to:
  1. Sign up and create their organization (if they haven’t already)
  2. Select their cloud provider and region
  3. Complete the setup to create their appliance
Their appliance will be provisioned automatically in their cloud account.
3

Monitor appliance creation

Check when the customer’s appliance is ready:
tensor9 report
Wait until their appliance status shows “Live”.
4

Create a release for the customer

Once their appliance is ready:
tensor9 stack release create \
  -appName my-app \
  -customerName acme-corp \
  -vendorVersion "1.0.0" \
  -description "Initial deployment for Acme Corp" \
  -notes "Configured for production use"
The deployment stack downloads to a directory named after their appliance.
5

Deploy to the customer's appliance

Deploy the release to the customer’s appliance:
cd acme-corp-appliance
tofu init
tofu apply
This deploys your application into the customer’s appliance.

Release to a customer appliance

Release infrastructure or code changes to your customer appliances.
1

Update your origin stack

Make changes to your Terraform files, docker-compose.yml, or CloudFormation template.
2

Republish the stack

For Terraform:
tensor9 stack publish \
  -stackType TerraformWorkspace \
  -stackS3Key my-app-stack \
  -dir .
For Docker Compose:
tensor9 stack publish \
  -stackType DockerCompose \
  -stackS3Key my-app-compose \
  -file docker-compose.yml
For CloudFormation:
aws cloudformation update-stack \
  --stack-name my-app-stack \
  --template-body file://template.yaml
You don’t need to re-bind the stack. Tensor9 will use the updated version for new releases.
3

Test in test appliance

Create a release to your test appliance:
tensor9 stack release create \
  -appName my-app \
  -testApplianceName my-test-appliance \
  -vendorVersion "1.1.0" \
  -description "Added new features" \
  -notes "Added caching layer and API improvements"
4

Deploy and verify

cd my-test-appliance
tofu apply
Verify the changes work as expected.
5

Release to customers

After testing, release to your customers:
# Release to specific customer
tensor9 stack release create \
  -appName my-app \
  -customerName acme-corp \
  -vendorVersion "1.1.0" \
  -description "Feature update" \
  -notes "New caching layer and performance improvements"

# Or release to all customers
tensor9 stack release create \
  -appName my-app \
  -all \
  -vendorVersion "1.1.0" \
  -description "Feature update" \
  -notes "New caching layer and performance improvements"

Manage multiple form factors

Deploy your app to different cloud providers or connectivity modes.
1

Create form factors

Create form factors for different environments:
# AWS connected
tensor9 form-factor create \
  -appName my-app \
  -formFactorName aws-connected \
  -description "AWS with internet connectivity" \
  -env Aws \
  -connectivity Connected

# GCP connected
tensor9 form-factor create \
  -appName my-app \
  -formFactorName gcp-connected \
  -description "Google Cloud with internet connectivity" \
  -env Gcp \
  -connectivity Connected

# DigitalOcean connected
tensor9 form-factor create \
  -appName my-app \
  -formFactorName digitalocean-connected \
  -description "DigitalOcean with internet connectivity" \
  -env DigitalOcean \
  -connectivity Connected
2

Test each form factor

Create test appliances for each form factor:
tensor9 test appliance create \
  -appName my-app \
  -name my-aws-test \
  -formFactorName aws-connected

tensor9 test appliance create \
  -appName my-app \
  -name my-gcp-test \
  -formFactorName gcp-connected \
  -cloudRegion gcp:us-central1
3

Release to each test appliance

tensor9 stack release create \
  -appName my-app \
  -testApplianceName my-aws-test \
  -vendorVersion "1.0.0" \
  -description "AWS test" \
  -notes "Testing AWS deployment"

tensor9 stack release create \
  -appName my-app \
  -testApplianceName my-gcp-test \
  -vendorVersion "1.0.0" \
  -description "GCP test" \
  -notes "Testing GCP deployment"

Onboard your vendor controller to Tailscale

By default, the listeners your CLI and Terraform use to reach the vendor controller are exposed on a public network load balancer with mTLS protection. If you operate a Tailscale tailnet for your engineering team, you can have the vendor controller join that tailnet and (optionally) remove the public path entirely. See Connectivity for the broader picture. This workflow rolls out in two phases: first attach the controller to your tailnet and verify operator access, then optionally enforce tunnel-only by removing the public listeners.

Phase 1: attach the vendor controller to your tailnet

1

Ensure your tailnet ACL has the required tags and rules

This step is idempotent. It adds the tag:tensor9-vctrl (vendor controller) and tag:tensor9-customer-ctrl (appliance controller) tags, the group:tensor9-operators group, and the rules that allow operators and customer appliances to reach the vendor controller. Existing ACL entries are preserved.
export TAILSCALE_API_KEY=tskey-api-...

tensor9 tailscale acl setup
If you maintain ACLs in HuJSON via the dashboard, the command prints a copy-pasteable snippet you can apply by hand. Otherwise answer y at the confirmation prompt to have the CLI POST the change for you (a local backup is written under ~/.tensor9/tailscale-acl-backups/).See tensor9 tailscale acl setup for full options.
2

Generate a pre-auth key for the vendor controller

The key is tagged so the ACL applies the right rules to the resulting node. Keep it single-use (the default) for production:
tensor9 tailscale key generate -tag VCtrl
Save the printed tskey-auth-... value; you will pass it to the next command. See tensor9 tailscale key generate for full options.Add operators to the group:operators group in the Tailscale dashboard so they can reach the controller’s listeners over the tailnet.
3

Attach the vendor controller to the tailnet

tensor9 vendor tailscale onboard \
  -vctrlKey tskey-auth-XXXXXXXXXXXX
The command installs the Tailscale daemon on the vendor controller, joins it to the tailnet, and stamps the resulting tailnet hostname onto the controller’s configuration. The controller’s existing public listeners stay in place.See tensor9 vendor tailscale onboard for full options.
4

Verify operator access over the tailnet

With your operator account in group:operators and connected to the tailnet, run a read-only command:
tensor9 report
Your CLI should be able to reach the vendor controller over the tailnet without any extra flags. The controller is still reachable over the public path too, so this step proves the tailnet route is working while leaving you a fallback.The endpoint used with Terraform/OpenTofu plan and apply operations is defined in the tensor9 provider block of the compiled deployment stack. Compilation emits the publicly available listener to this block when it is available, even if you have a tunnel configured, to be compatible with CI deployments. For testing, you can manually modify the compiled stack to change the endpoint address to be the vendor controller’s address on the tailnet and try a plan. Follow phase 2, below, to remove the TF endpoint from the public load balancer and re-compile your deployment stacks to enforce using the Tailscale for communication between your local Terraform/OpenTofu CLI and the vendor controller.

Phase 2: remove the public listeners

Once you are satisfied that operators and Terraform-driven deploys work over the tailnet, you can enforce tunnel-only and tear the public listeners down. Roll this out per listener group rather than all at once so you can pause if something is missed.
1

Mark the operator listeners as tunnel-only

The tunnel enforce command mutates the controller’s configuration; the listener teardown happens on the next infrastructure upgrade.
# CLI listeners first
tensor9 vendor tunnel enforce -add CLI

# After a soak period, the Terraform reactor too (make sure CI works!)
tensor9 vendor tunnel enforce -add Terraform
See tensor9 vendor tunnel enforce for full options. If you also want to remove the appliance-facing public listener, the command checks first that no customer appliance still depends on it; see the pre-flight notes in the reference.
2

Apply the change

Run an infrastructure upgrade to remove the now-redundant listeners from your network load balancer:
tensor9 vendor upgrade \
  -kind Infrastructure \
  -reason "remove public CLI and Terraform listeners after Tailscale cutover"
After this completes, the only way to reach the enforced listeners is over the tailnet.
Make sure every operator and every CI runner that drives tensor9 or terraform against the vendor controller is on the tailnet before you run tunnel enforce -add CLI / Terraform. After the next infrastructure upgrade, anything off the tailnet will be locked out.
To roll back, run tensor9 vendor tunnel enforce -remove CLI,Terraform followed by tensor9 vendor upgrade -kind Infrastructure. The public listeners are recreated.

Use stack tuning documents

Customize resource allocations per customer or environment.
1

Create a tuning document

Create a JSON file with resource overrides:
{
  "version": "V1",
  "composeServices": {
    "web": {
      "replicas": 4,
      "resources": {
        "cpu": "2",
        "memory": "4Gi"
      }
    },
    "api": {
      "replicas": 3,
      "resources": {
        "cpu": "1",
        "memory": "2Gi"
      }
    }
  }
}
Save as enterprise-tuning.json.
2

Release with tuning document

tensor9 stack release create \
  -appName my-app \
  -customerName enterprise-customer \
  -vendorVersion "1.0.0" \
  -description "Enterprise deployment" \
  -notes "High-performance configuration" \
  -tuningDoc enterprise-tuning.json
The deployment stack will use the tuned resource specifications.

Monitor appliances

Check the status of your apps, appliances, and releases.

View comprehensive report

tensor9 report
This shows:
  • All your apps and their stacks
  • All test appliances and their status
  • All customer appliances and their status
  • Active releases

View detailed report

tensor9 report -detailed

List all appliances

tensor9 appliance list

Use the web portal

Start a local web interface:
tensor9 portal
This opens a browser with a visual dashboard of your apps and appliances.

Retire a test appliance

Remove a test appliance when no longer needed.
tensor9 test appliance retire -testApplianceName my-test-appliance
This permanently deletes the test appliance and all its infrastructure. It may take several minutes to fully deprovision.

Troubleshoot common issues

Problem: tofu apply fails when deploying a release.Solution:
  1. Check the Terraform error messages
  2. Verify your origin stack is valid: tofu validate in your workspace
  3. Check appliance status: tensor9 report
  4. Review deployment stack variables and configuration
  5. Check AWS credentials and permissions
Problem: Test appliance remains in “Creating” status for a long time.Solution:
  1. Wait 15-20 minutes (appliance creation can take time)
  2. Check tensor9 report -detailed for error messages
  3. Verify AWS quotas are sufficient for EKS, VPCs, etc.
  4. Check CloudFormation console in AWS for stack creation issues
  5. Contact support if stuck for more than 30 minutes
Problem: tensor9 stack publish fails with upload errors.Solution:
  1. Verify AWS credentials: aws sts get-caller-identity --profile <profile>
  2. Check that no .terraform directories are in your workspace
  3. Ensure you have write permissions to the control plane S3 bucket
  4. For large stacks, check your network connection
Problem: Customer reports they can’t reach their deployed application.Solution:
  1. Verify deployment completed: Check with customer that tofu apply succeeded
  2. Check load balancer: kubectl get service shows external IP/hostname
  3. Verify DNS configuration if using custom domains
  4. Check security groups/firewall rules in customer’s cloud
  5. Review application logs: kubectl logs <pod-name>
Problem: After creating a release, the deployment stack doesn’t download.Solution:
  1. Wait a few minutes (compilation takes time)
  2. Check release status: tensor9 report
  3. Verify appliance is in “Live” status
  4. Check for stack validation errors in the report
  5. Review control plane CloudWatch logs for compilation errors

Best practices

Use semantic versioning for your releases:
  • 1.0.0 - Initial release
  • 1.0.1 - Patch (bug fixes)
  • 1.1.0 - Minor (new features, backward compatible)
  • 2.0.0 - Major (breaking changes)
This helps customers understand the impact of updates.
Always create a release to a test appliance before releasing to customers:
  1. Create release to test appliance
  2. Deploy and verify functionality
  3. Test upgrade path from previous version
  4. Only then release to customers
Write clear, customer-facing release notes:
tensor9 stack release create \
  -appName my-app \
  -customerName acme-corp \
  -vendorVersion "1.2.0" \
  -description "Bug fixes and performance improvements" \
  -notes "Fixed authentication timeout issue. Improved API response time by 40%. Added support for batch operations."
Your customers see these notes when deploying.
Keep your origin stacks in version control and use consistent naming:
  • Use Git to track changes to origin stacks
  • Tag releases in Git: git tag v1.0.0
  • Use the same version in Git and Tensor9 releases
  • Document infrastructure changes in commit messages
Regularly check appliance status:
# Weekly check
tensor9 report

# Monthly detailed review
tensor9 report -detailed
Set up alerts for customer appliance issues.

Next steps