Overview
This quickstart demonstrates how workloads communicate internally with automatic mTLS encryption. By default, workloads are isolated and reject internal traffic. You’ll configure firewall rules to enable secure service-to-service calls.
What you’ll accomplish:
- Deploy a new workload that calls an existing service
- Observe the default deny behavior
- Configure internal firewall rules to allow communication
- Verify secure service-to-service calls
Prerequisites
How Internal Communication Works
Workloads communicate using internal endpoints following this pattern:
http://WORKLOAD_NAME.GVC_NAME.cpln.local:PORT
All internal traffic is automatically encrypted with mTLS. No certificates to manage.
So far, you’ve created a GVC (quickstart-gvc) and deployed a workload (hello-world). Now you’ll create a new workload that communicates with the existing hello-world workload.
Console UI
CLI
Terraform
Pulumi
Step 1: Create the Caller Workload
Navigate to Workloads
Click Workloads in the left menu, then click New.
Configure workload basics
- Name:
caller
- Make sure
quickstart-gvc is selected in the GVC dropdown
Configure container
- Click
Containers in the left pane
- Select
External and enter the image: cplnquickstarts/service-to-service-quick-start:1.1
- Under
Ports, set Protocol to http and Number to 8080
Configure firewall and create
- Click
Firewall in the left pane
- Click
Make Public
- Click
Create
Step 2: Observe Default Deny Behavior
Wait for workload to be ready
The caller workload shows Ready in Workload Health.
Open the caller workload
Navigate to the caller workload and click Open next to the Global Endpoint.
Test internal call
Add this query parameter to the caller URL:?url=http://hello-world.quickstart-gvc.cpln.local:8080
The request will timeout and fail because the hello-world workload blocks internal traffic by default. Navigate to hello-world workload
Click Workloads in the left menu and select hello-world.
Configure firewall
- Click
Firewall in the left pane
- Under the
Internal tab, select Same GVC for the Inbound Allow Type
- Click
Update
Step 4: Verify Communication
Wait for redeployment
The hello-world workload redeploys with new firewall rules (1-2 minutes).
Test again
Refresh the caller page with the URL parameter. The hello-world workload now responds:Response from URL:
---
Hello World!
---
Step 1: Create the Caller Workload
cpln workload create --name caller --gvc quickstart-gvc \
--image cplnquickstarts/service-to-service-quick-start:1.1 \
--port 8080 \
--public
Step 2: Test Default Deny
Get the caller endpoint:cpln workload get caller --gvc quickstart-gvc
Open the endpoint from the table in your browser and add:?url=http://hello-world.quickstart-gvc.cpln.local:8080
The request will timeout and fail because the hello-world workload blocks internal traffic by default.Step 3: Enable Internal Access
Update the hello-world firewall to allow internal traffic:cpln workload update hello-world --gvc quickstart-gvc --set spec.firewallConfig.internal.inboundAllowType=same-gvc
Step 4: Verify Communication
Wait 1-2 minutes for redeployment, then refresh the caller page. The hello-world workload now responds:Response from URL:
---
Hello World!
---
Step 1: Add the Caller Workload
Add to your existing main.tf from Quickstart 1:# Caller workload
resource "cpln_workload" "caller" {
gvc = cpln_gvc.quickstart.name
name = "caller"
type = "standard"
container {
name = "main"
image = "cplnquickstarts/service-to-service-quick-start:1.1"
cpu = "50m"
memory = "128Mi"
ports {
protocol = "http"
number = 8080
}
}
options {
capacity_ai = true
timeout_seconds = 5
autoscaling {
metric = "disabled"
target = 95
min_scale = 1
max_scale = 1
}
}
firewall_spec {
external {
inbound_allow_cidr = ["0.0.0.0/0"]
}
}
}
output "caller_endpoint" {
value = cpln_workload.caller.status[0].canonical_endpoint
}
Apply the configuration:Step 2: Test Default Deny
Open the caller endpoint and add:?url=http://hello-world.quickstart-gvc.cpln.local:8080
The request will timeout and fail because the hello-world workload blocks internal traffic by default.Step 3: Enable Internal Access
Update your cpln_workload.hello-world resource to allow internal traffic. Add the internal block inside firewall_spec:firewall_spec {
external {
inbound_allow_cidr = ["0.0.0.0/0"]
}
# Add internal firewall to allow traffic from same GVC
internal {
inbound_allow_type = "same-gvc"
}
}
Apply the changes:Step 4: Verify Communication
Wait 1-2 minutes for redeployment, then refresh the caller page. The hello-world workload now responds:Response from URL:
---
Hello World!
---
Step 1: Add the Caller Workload
Add to your existing Pulumi project from Quickstart 1:// Caller workload
const caller = new cpln.Workload("caller", {
gvc: gvc.name,
name: "caller",
type: "standard",
containers: [
{
name: "main",
image: "cplnquickstarts/service-to-service-quick-start:1.1",
cpu: "50m",
memory: "128Mi",
ports: [{ protocol: "http", number: 8080 }],
},
],
options: {
capacityAi: true,
timeoutSeconds: 5,
autoscaling: {
metric: "disabled",
target: 95,
minScale: 1,
maxScale: 1,
},
},
firewallSpec: {
external: {
inboundAllowCidrs: ["0.0.0.0/0"],
},
},
});
export const callerEndpoint = caller.statuses.apply(
(s) => s?.[0]?.canonicalEndpoint ?? "pending"
);
# Caller workload
caller = cpln.Workload("caller",
gvc=gvc.name,
name="caller",
type="standard",
containers=[cpln.WorkloadContainerArgs(
name="main",
image="cplnquickstarts/service-to-service-quick-start:1.1",
cpu="50m",
memory="128Mi",
ports=[cpln.WorkloadContainerPortArgs(
protocol="http",
number=8080,
)],
)],
options=cpln.WorkloadOptionsArgs(
capacity_ai=True,
timeout_seconds=5,
autoscaling=cpln.WorkloadOptionsAutoscalingArgs(
metric="disabled",
target=95,
min_scale=1,
max_scale=1,
),
),
firewall_spec=cpln.WorkloadFirewallSpecArgs(
external=cpln.WorkloadFirewallSpecExternalArgs(
inbound_allow_cidrs=["0.0.0.0/0"],
),
))
pulumi.export("caller_endpoint", caller.statuses[0].canonical_endpoint)
// Caller workload
caller, err := cpln.NewWorkload(ctx, "caller", &cpln.WorkloadArgs{
Gvc: gvc.Name,
Name: pulumi.String("caller"),
Type: pulumi.String("standard"),
Containers: cpln.WorkloadContainerArray{
&cpln.WorkloadContainerArgs{
Name: pulumi.String("main"),
Image: pulumi.String("cplnquickstarts/service-to-service-quick-start:1.1"),
Cpu: pulumi.String("50m"),
Memory: pulumi.String("128Mi"),
Ports: cpln.WorkloadContainerPortArray{
&cpln.WorkloadContainerPortArgs{
Protocol: pulumi.String("http"),
Number: pulumi.Int(8080),
},
},
},
},
Options: &cpln.WorkloadOptionsArgs{
CapacityAi: pulumi.Bool(true),
TimeoutSeconds: pulumi.Int(5),
Autoscaling: &cpln.WorkloadOptionsAutoscalingArgs{
Metric: pulumi.String("disabled"),
Target: pulumi.Int(95),
MinScale: pulumi.Int(1),
MaxScale: pulumi.Int(1),
},
},
FirewallSpec: &cpln.WorkloadFirewallSpecArgs{
External: &cpln.WorkloadFirewallSpecExternalArgs{
InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
},
},
})
if err != nil {
return err
}
ctx.Export("caller_endpoint", caller.Statuses.Index(pulumi.Int(0)).CanonicalEndpoint())
// Caller workload
var caller = new Workload("caller", new WorkloadArgs
{
Gvc = gvc.Name,
Name = "caller",
Type = "standard",
Containers = new[]
{
new WorkloadContainerArgs
{
Name = "main",
Image = "cplnquickstarts/service-to-service-quick-start:1.1",
Cpu = "50m",
Memory = "128Mi",
Ports = new[]
{
new WorkloadContainerPortArgs
{
Protocol = "http",
Number = 8080
}
}
}
},
Options = new WorkloadOptionsArgs
{
CapacityAi = true,
TimeoutSeconds = 5,
Autoscaling = new WorkloadOptionsAutoscalingArgs
{
Metric = "disabled",
Target = 95,
MinScale = 1,
MaxScale = 1
}
},
FirewallSpec = new WorkloadFirewallSpecArgs
{
External = new WorkloadFirewallSpecExternalArgs
{
InboundAllowCidrs = new[] { "0.0.0.0/0" }
}
}
});
Add to your return dictionary:["caller_endpoint"] = caller.Statuses.Apply(s => s[0].CanonicalEndpoint)
Deploy the configuration:Step 2: Test Default Deny
Open the caller endpoint and add:?url=http://hello-world.quickstart-gvc.cpln.local:8080
The request will timeout and fail because the hello-world workload blocks internal traffic by default.Step 3: Enable Internal Access
Update your hello-world workload to allow internal traffic. Add the internal block inside firewallSpec:firewallSpec: {
external: {
inboundAllowCidrs: ["0.0.0.0/0"],
},
// Add internal firewall to allow traffic from same GVC
internal: {
inboundAllowType: "same-gvc",
},
},
firewall_spec=cpln.WorkloadFirewallSpecArgs(
external=cpln.WorkloadFirewallSpecExternalArgs(
inbound_allow_cidrs=["0.0.0.0/0"],
),
# Add internal firewall to allow traffic from same GVC
internal=cpln.WorkloadFirewallSpecInternalArgs(
inbound_allow_type="same-gvc",
),
)
FirewallSpec: &cpln.WorkloadFirewallSpecArgs{
External: &cpln.WorkloadFirewallSpecExternalArgs{
InboundAllowCidrs: pulumi.StringArray{pulumi.String("0.0.0.0/0")},
},
// Add internal firewall to allow traffic from same GVC
Internal: &cpln.WorkloadFirewallSpecInternalArgs{
InboundAllowType: pulumi.String("same-gvc"),
},
},
FirewallSpec = new WorkloadFirewallSpecArgs
{
External = new WorkloadFirewallSpecExternalArgs
{
InboundAllowCidrs = new[] { "0.0.0.0/0" }
},
// Add internal firewall to allow traffic from same GVC
Internal = new WorkloadFirewallSpecInternalArgs
{
InboundAllowType = "same-gvc"
}
}
Deploy the changes:Step 4: Verify Communication
Wait 1-2 minutes for redeployment, then refresh the caller page. The hello-world workload now responds:Response from URL:
---
Hello World!
---
Internal Firewall Options
| Option | Description |
|---|
| None (default) | Block all internal traffic |
| same-gvc | Allow from any workload in the same GVC |
| same-org | Allow from any workload in the organization |
| workload-list | Allow from specific workloads only |
The workload-list option requires view permission on the allowed workloads.
Workloads communicate using internal DNS:
http://WORKLOAD.GVC.cpln.local:PORT
Examples:
http://api.production-gvc.cpln.local:8080
http://cache.quickstart-gvc.cpln.local:6379
What You’ve Learned
- Workloads are isolated by default - internal traffic is blocked
- mTLS is automatic - no certificate configuration needed
- Firewall rules control which workloads can communicate
- Internal endpoints use the
.cpln.local domain
Clean Up
To delete all resources created in the quickstart series:
Console UI
CLI
Terraform
Pulumi
- Navigate to
GVCs, select quickstart-gvc, and from Actions click Delete
- Navigate to
Domains, select your domains, and from Actions click Delete
cpln gvc delete quickstart-gvc
cpln domain delete app.example.com
cpln domain delete example.com
Remember to remove the DNS records from your DNS provider after deleting the domains.
Next Steps