Using additional TCP ports in Azure Container Apps
This post will go over some general ways to use the ‘additional TCP ports’ option on Azure Container Apps
Overview
You can set up your Container App to use more than just one TCP port when ingress is enabled. This can be used when the ingress type is either ‘HTTP’ or ‘TCP’. Additionally, depending on if the Container App Environment is VNET injected, these additional TCP ports can be externally available. If your Container App Environment is not VNET injected, then these ports cannot be called externally, only internally between applications.
Additional information and limitations of this are explained here in Additional TCP ports
With a VNET
The biggest difference between using a VNET and without one is the ability to connect to TCP ports from outside of the environment - as denoted by the “Accepting traffic from anywhere”
A basic example of using additional TCP ports from the Azure Portal would look like the below:

This example infers that the applications main ingress is reacheable over HTTP. It then exposes two additional TCP ports that are accessible over the value defined in Exposed port externally.

This example infers that the applications main ingress is reacheable over TCP. It then exposes two additional TCP ports that are accessible over the value defined in Exposed port externally.

The different port numbers between Target port and Exposed port may be interesting, but can be explained as this:
- Target portis a required field. At a minimum, if using additional ports, this needs to be set.
- Exposed portis not a required field - connections will be made to the value defined in- Target portif ommitted.
- If your Container App Environment is VNET injected, then you can use the value set in Exposed portto be what outside applications or clients use to reach the application. Although this doesn’t need to be necessarily outside of the environment and can be reached the same way from within the environment.- For example, port 8991is what we can connect to the application from externally, this will essentially forward the request to theTarget portof8090
- Port 8992is another example of a port we can connect to, which essentially forwards to port8080.
 
- For example, port 
Note, that you do not need to do this. Regardless if you’re using Exposed port or not - the application should be expected to accept and listen for connections on the value(s) defined in Target port for its TCP server(s). If the value in Exposed port is different than Target port - the application doesn’t actually need to listen for connections on Exposed port within its code.
The general syntax for reaching these exposed ports would be:
- External and by full FQDN (when VNET is used and ingress is external): myapp.funnyname-1234abc.region.azurecontainerapps.io:[port_number]
- Internal and by app name (when VNET is used and ingress is internal only or calling by inter-environment): myapp:[port_number]
Be aware that if you use a value for  that’s not the same value in Target port, connecting directly to the value in Target port will time out. Either from externally or internally - since the application is now accessible over what’s defined in Exposed port
Without a VNET
As mentioned earlier, if a VNET is not used with the environment, these Exposed port TCP ports cannot be accessed externally. However, from an internal/inter-environment standpoint, the functionality mostly remains the same.
You’ll notice that changed the Ingress traffic field will be grayed out and limited to “Limited to Container Apps Environment”
- This example shows basic usage with only Target portbeing used

- This example shows basic usage with both Target portandExposed portbeing used

Calling between applications would be done by the application name you want to target. Eg., nc -v myapp 8080. Or, using an example with a Go TCP client implementation, would look like the below:
tcpClient, err := net.ResolveTCPAddr("tcp", "myapp:8080")
Other language syntax will vary but the concept is the same.
You can still utilize Exposed port without a VNET as long as the calling application or client lives within the environment and is calling by the app name - eg. nc -v myapp 8991, which given the above screenshot, will ultimately connecto the applications target port of 8080.
If you try to call to these Export ports from outside the environment, the TCP connection will fail and/or time out.
Static IP
You can connect to your applications by using the static IP instead of FQDN as well when a VNET is used on the environment. This mimics the same discussion in the above With a VNET section.
To find your static IP, go to the Container App Environment -> Overview -> Static IP. Using the examples above and below in this blog, you’ll see the same behavior:

Examples
We’ll use the below Go application to see some of this in action. These examples are using the Exposed and Target ports set earlier above. In  scenario where Exposed Port isn’t used - calling the app is straight forward you where just the value defined in Target Port is used.
This first example uses Fiber to run an HTTP server and creates two additional TCP servers to listen on ports 8080 and 8090.
func handleRequest(conn net.Conn, server string) {
	// close conn
	defer conn.Close()
	// write data to response
	time := time.Now().Format(time.ANSIC)
	res := "TCP server " + server + ": Recieved connection at: " + time
	zap.L().Info("TCP server " + server + ": Recieved connection at: " + time)
	conn.Write([]byte(res))
}
func tcpServerOne() {
	HOST := "0.0.0.0"
	PORT := "8080"
	TYPE := "tcp"
	SERVER := "[1]"
	zap.L().Info("TCP server " + SERVER + " is listening on port " + PORT)
	listen, err := net.Listen(TYPE, HOST+":"+PORT)
	if err != nil {
		zap.L().Fatal(err.Error())
	}
	// close listener
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			zap.L().Fatal(err.Error())
		}
		go handleRequest(conn, SERVER)
	}
}
func tcpServerTwo() {
	HOST := "0.0.0.0"
	PORT := "8090"
	TYPE := "tcp"
	SERVER := "[2]"
	zap.L().Info("TCP server " + SERVER + " is listening on port " + PORT)
	listen, err := net.Listen(TYPE, HOST+":"+PORT)
	if err != nil {
		zap.L().Fatal(err.Error())
	}
	// close listener
	defer listen.Close()
	for {
		conn, err := listen.Accept()
		if err != nil {
			zap.L().Fatal(err.Error())
		}
		go handleRequest(conn, SERVER)
	}
}
func main() {
... truncating other code
	app := fiber.New()
	app.Get("/", controllers.IndexController)
    // Start multiple tcp servers
	go tcpServerOne()
	go tcpServerTwo()
	zap.L().Info("Fiber (HTTP) server is running on port 3000")
	fiberErr := app.Listen(":3000")
	if fiberErr != nil {
		zap.L().Fatal(fiberErr.Error())
	}
}
- TCP server one [1] listens on port 8080
- TCP server two [2] litens on port 8090
Using nc (or you can use any other TCP client in a client application), we can see the port forwarding in action:

- We can see that connecting to our Exposed porton the Container App of8991is ultimately connecting to port8090ontcpServerTwo()within our application.

- We see the same here, where connecting to our Exposed porton the Container App of8992is ultimately connecting to port8080ontcpServerOne()within our application.

- The same behavior is seen for inter-environment calls when just using the name of your target Container App
Troubleshooting
Below are some common scenarios and potential reasons for them.
No connection could be made because the target machine actively refused it:
- Ingress is disabled
- A TCP connection is trying to be made to an ingress type of ‘HTTP’ and that port requested is not exposed
- The TCP port being connected to is not exposed, in general
- Making a request to an external TCP port but the Ingress and/or specific target port is set to ‘internal’ - or - the environment is not in a VNET
No such host is known:
- The client cannot resolve the DNS name. Ensure the FQDN is not mistyped.
- Check if a custom DNS server is used. These may not be resolving Azure hostnames properly.
No connection error but no data received from server or the client “hangs” with no response when connecting:
- Ingress is disabled
- A TCP connection is trying to be made to an ingress type of ‘HTTP’ and that port requested is not exposed
- The TCP port being connected to is not exposed, in general
- Making a request to an external TCP port but the Ingress and/or specific target port is set to ‘internal’ - or - the environment is not in a VNET
- The connection is not recieving data and being terminated by the default 240 second timeout limit for ingress.
 
       
      