This runbook covers a Container Apps workload-profiles environment with an internal virtual-network entry, reached from a laptop over an approved point-to-site VPN, using the app's direct FQDN and no environment-level HTTP routes. Three things must be true together: the environment entry is reachable from the client, the app's ingress scope admits the caller, and the hostname resolves to the private entry. Each step below leaves a verifiable result and a rollback that does not touch anything you did not change. Work through them in the given order, because no later step can compensate for a scope decision made wrongly in step 1.

Before you touch DNS, settle the word internal. It is used for two different scopes in this product, and swapping them is the most common reason a working name still returns nothing useful.

Step 1: fix the environment and app scope

Read the app's ingress configuration before changing any record:

SettingBoundary
Internal environmentThe environment entry is reached through its private network arrangement
App with internal ingressCallers from the environment VNet only; a VPN laptop qualifies once it has a route into that VNet and the domain resolves privately
App with external ingress in an internal environmentThe app accepts outside traffic through the environment's private entry

Internal ingress is exposed on the environment's internal load balancer inside the environment VNet, so the question is not whether the laptop is another container app but whether it can reach that VNet. A VPN client with a route into the VNet and the environment default domain resolving through the private zone is an eligible caller, which is what the remaining steps build. An internal environment with no public entry is a different fact from an app with internal ingress, and external ingress at the app layer does not give the environment a public door. Write the scope into the ticket as well as the portal, because the next engineer reads the ticket first.

Confirm that scope with the configuration owner before you continue, and keep the gate narrow: if no route into the environment VNet exists, or the environment default domain does not resolve through the private zone, stop and fix that path first. No later step can substitute for a caller that cannot reach the VNet or resolve the name.

Step 2: collect the real hostname and entry address

Record the environment's default domain, its static private entry address and the app's configured FQDN, taken from the resource rather than from an old screenshot or a wiki page:

az containerapp env show -n ENV -g RG \
  --query "{domain:properties.defaultDomain, ip:properties.staticIp}"
az containerapp show -n APP -g RG \
  --query "properties.configuration.ingress.fqdn"

The Container Apps private-entry guidance describes matching the environment's domain to its internal address while keeping the application hostnames intact. That is exactly the shape you are building by hand here, so align the zone contents with it instead of inventing a naming scheme.

If the domain you read does not match the FQDN the app reports, stop and reconcile them now. Building a zone against the wrong suffix produces a hostname that resolves perfectly and still lands on the wrong virtual host.

Step 3: put the private zone on the VPN's resolver path

Create or reuse the supported private DNS zone for the environment's actual default domain, with the documented wildcard and address records pointing at the static entry. Link that zone to the appropriate virtual network, and make the private authority reachable through the VPN's approved resolver path rather than assuming the link is enough.

A point-to-site client usually needs a private-resolver inbound endpoint or a supported DNS forwarder to reach that zone. Linking a zone changes what Azure resolves for linked networks; it does not redirect every external laptop's public resolver. Do not substitute a private-endpoint DNS zone borrowed from a different Container Apps networking model, because the record names and the zone suffix will not line up.

Rollback for this step is removing the zone link or the record you added. Keep note of which it was, since deleting a shared zone to undo a single link is a well-known way to break a second environment.

Step 4: test the name, then the denied audience

Test the hostname and TLS identity together, not the address alone. For a controlled name-mapping diagnostic you can pin the address while preserving the hostname:

curl --resolve app.ENVIRONMENT_DOMAIN:443:10.40.0.10 \
    https://app.ENVIRONMENT_DOMAIN/

Replace the hostname with the real app FQDN and the address with the real internal entry. This affects that one request and is not DNS provisioning. Do not add -k to turn an identity failure into an apparent pass, because a certificate mismatch is the signal that you are hitting the wrong listener.

Requesting the numeric address directly can select the wrong virtual host or fail validation even while TCP 443 answers, so treat a successful IP test as evidence about the network and nothing more. From the intended laptop, the normal resolver path is the client DNS forwarder or the private resolver inbound endpoint that hosts the zone, and a failure there is a resolver reachability problem rather than a missing record. Confirm the same request succeeds on that path, then confirm the denied audience is still denied.

What to record, and what to roll back

The container target port and the client's HTTPS port are separate settings. Do not publish the container's target port across the VPN because the container listens there, and do not widen a network rule to make a port test pass. Both changes outlive the incident and are hard to attribute later.

Finish with a fresh application operation from the intended laptop, using its authorized identity and its normal DNS, then check that an unauthorized caller still fails. While you are in the app configuration, also confirm whether an environment-level HTTP route exposes this app as a target, since that route is a second door the ingress scope does not describe.

Record five values together: environment scope, app ingress scope, FQDN, private entry address and resolver owner. Those five describe the whole access contract, and the next person to debug it will ask for exactly them.

פֿאַרבונדענע אַרטיקלען