Handling HTTP and HTTPS with an Application Load Balancer and Traefik¶
When you install K3s with the default configuration, it typically includes the Traefik ingress controller. This component listens on ports 80 and 443 inside the cluster for incoming HTTP and HTTPS connections. By default, K3s might expose Traefik via a Service
of type LoadBalancer
or NodePort
, depending on your configuration. Below, we’ll assume NodePort and demonstrate a manually created Application Load Balancer that forwards external traffic to Traefik’s NodePorts on each cluster node.
Tip: If you prefer a more automated approach, you can use the AWS Load Balancer Controller for Kubernetes, which automatically creates or configures an ALB when you define a
Service
of typeLoadBalancer
. The manual steps below are helpful for learning or more custom setups.
1. Confirm That Traefik Is Installed¶
1.1 Check if Traefik is running in your K3s cluster¶
You should see one or more pods named something like traefik-xxxxxx
.
1.2 Confirm Traefik’s Service¶
Look for a Service
named traefik
in the kube-system
or traefik
namespace. It might be:
type: NodePort
, ortype: LoadBalancer
(in which case AWS may already have created an ALB for you).
If you see type: NodePort
, take note of the NodePort values for ports 80 and 443. For example:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
traefik NodePort 10.43.156.164 <none> 80:31380/TCP, 443:31443/TCP 1d
- HTTP is on port
31380
. - HTTPS is on port
31443
.
If the service is already
LoadBalancer
, you may not need to create an ALB manually—K3s might have done it (depending on your cluster’s cloud controller). You can skip to setting up DNS and certificates.
2. Open the NodePort Range in the Security Group¶
Since you’ll forward traffic from the ALB to each node’s NodePort, make sure your cluster’s Security Group rules allow inbound traffic on those ports (e.g., 31380 and 31443, or whichever NodePort values Traefik is using).
Example (assuming the same security group $SG_ID
for your nodes):
# Allow the NodePort range or specific NodePorts
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 31380 \
--source-group $SG_ID
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 31443 \
--source-group $SG_ID
If multiple nodes exist, the ALB needs to connect to each node’s NodePort. Opening these ports within the same security group ensures the load balancer instances can communicate with the cluster nodes.
3. Create an Application Load Balancer (ALB)¶
You will create two listeners on the ALB:
- Listener on port 80 (HTTP), forwarding to Traefik’s NodePort for HTTP.
- Listener on port 443 (HTTPS), forwarding to Traefik’s NodePort for HTTPS.
[!TIP] If you plan to let Traefik handle TLS certificates (e.g., Let’s Encrypt), you can do a TCP pass-through of HTTPS traffic or simply forward port 443 with an HTTP target group. Alternatively, you can terminate TLS at the ALB itself (upload certificates to AWS Certificate Manager) and forward HTTP to Traefik inside the cluster. We’ll show a simple pass-through approach below so Traefik can manage certs.
3.1 Create the ALB Itself¶
LB_ARN=$(aws elbv2 create-load-balancer \
--name memvergeai-alb \
--type application \
--scheme internet-facing \
--subnets $SUBNET_ID \
--security-groups $SG_ID \
--query 'LoadBalancers[0].LoadBalancerArn' \
--output text)
echo "Created Application Load Balancer ARN: $LB_ARN"
- We use
--scheme internet-facing
to allow external internet traffic. - Attach it to your existing subnet (
$SUBNET_ID
) and security group ($SG_ID
).
3.2 Create Target Groups for HTTP (NodePort) and HTTPS (NodePort)¶
We’ll create two distinct target groups: one for port 31380 (HTTP) and one for port 31443 (HTTPS). Note these NodePort values should match the actual ones reported by the Traefik service in Step 1.
3.2.1 Target Group for HTTP¶
TG_HTTP_ARN=$(aws elbv2 create-target-group \
--name memvergeai-traefik-http \
--protocol HTTP \
--port 31380 \
--target-type instance \
--vpc-id $VPC_ID \
--query 'TargetGroups[0].TargetGroupArn' \
--output text)
echo "Created HTTP Target Group: $TG_HTTP_ARN"
3.2.2 Target Group for HTTPS¶
TG_HTTPS_ARN=$(aws elbv2 create-target-group \
--name memvergeai-traefik-https \
--protocol TCP \
--port 31443 \
--target-type instance \
--vpc-id $VPC_ID \
--query 'TargetGroups[0].TargetGroupArn' \
--output text)
echo "Created HTTPS Target Group: $TG_HTTPS_ARN"
[!NOTE] Why different protocols?
- HTTP on port 31380: We use ALB’s HTTP protocol for health checks and normal traffic.
- HTTPS pass-through on port 31443: Because we want Traefik to handle TLS, we set the ALB listener to TCP or “TLS” mode for direct pass-through. If you prefer to terminate TLS at the ALB, you’d choose
HTTPS
protocol and supply an ACM certificate.
4. Register Cluster Nodes to Each Target Group¶
All nodes that run the Traefik pods (often every node in a small K3s cluster) should be registered:
# Grab instance IDs for each node (management + worker if they both run Traefik)
INSTANCE_IDS=$(aws ec2 describe-instances \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query "Reservations[].Instances[].InstanceId" \
--output text)
# Register each instance to the HTTP target group
aws elbv2 register-targets \
--target-group-arn $TG_HTTP_ARN \
--targets $(for i in $INSTANCE_IDS; do echo "Id=$i,Port=31380"; done)
# Register each instance to the HTTPS target group
aws elbv2 register-targets \
--target-group-arn $TG_HTTPS_ARN \
--targets $(for i in $INSTANCE_IDS; do echo "Id=$i,Port=31443"; done)
[!NOTE] This step is manual. If you later add or remove nodes, you must update the target groups. An alternative approach is to use the AWS Load Balancer Controller which auto-registers new nodes.
5. Create Listeners on the ALB (Ports 80 and 443)¶
Now we configure how the ALB receives traffic from the internet and forwards to the NodePorts:
5.1 HTTP Listener (Port 80)¶
aws elbv2 create-listener \
--load-balancer-arn $LB_ARN \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=$TG_HTTP_ARN
- Requests arriving at
http://<ALB_DNS_NAME>
on port 80 get forwarded to Traefik’s HTTP NodePort (31380).
5.2 HTTPS Listener (Port 443)¶
If you want the ALB to simply pass through HTTPS traffic to Traefik for TLS termination:
aws elbv2 create-listener \
--load-balancer-arn $LB_ARN \
--protocol TCP \
--port 443 \
--default-actions Type=forward,TargetGroupArn=$TG_HTTPS_ARN
Note: An Application Load Balancer typically works at Layer 7 (HTTP/HTTPS). However, if you want to offload TLS at Traefik, you can choose the ALB’s “TLS” or “TCP” listener. “TLS” is layer 4 pass-through for the ALB.
If you prefer the ALB to terminate TLS, you’d use--protocol HTTPS
with an ACM certificate and forward HTTP to port 31443. In that scenario, you’d configure separate health checks and possibly different Ingress settings in Traefik.
6. Verify the ALB and Traefik Connectivity¶
Retrieve the ALB DNS Name¶
LB_DNS=$(aws elbv2 describe-load-balancers \
--load-balancer-arns $LB_ARN \
--query "LoadBalancers[0].DNSName" \
--output text)
echo "Your ALB DNS is: $LB_DNS"
Check Target Health¶
aws elbv2 describe-target-health --target-group-arn $TG_HTTP_ARN
aws elbv2 describe-target-health --target-group-arn $TG_HTTPS_ARN
The instances should report as healthy once Traefik is listening on those NodePorts.
Test HTTP¶
If there is a default HTTP route in Traefik or a default Ingress route, you should see an HTTP 200 or similar response.
If using pass-through for HTTPS, open https://
$LB_DNS
in a browser. Traefik should respond, though you might get a self-signed certificate error unless you’ve configured Let’s Encrypt or another TLS certificate in Traefik.
7. Create or Update DNS Records¶
Like in previous steps, you can create an A record or CNAME in Route53 (or your preferred DNS provider) pointing your chosen domain or subdomain (e.g., demo.example.com
) to the ALB’s DNS name:
aws route53 change-resource-record-sets \
--hosted-zone-id $HOSTED_ZONE_ID \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "demo.example.com.",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z26RNL4JYFTOTI", # ALB HostedZoneId for your region
"DNSName": "'"$LB_DNS"'",
"EvaluateTargetHealth": false
}
}
}]
}'
Replace demo.example.com
with your desired host name.
8. Define Ingress Resources in K3s¶
To route traffic to your internal services, you’ll create a Kubernetes Ingress resource. For example:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-web-app
namespace: default
spec:
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-web-service
port:
number: 80
Apply it:
- Traefik will see this Ingress definition and route
demo.example.com
traffic to the service namedmy-web-service
in port 80. - If you have TLS configured with Let’s Encrypt in Traefik, you can add a
tls:
block so Traefik presents a valid certificate.
9. Confirm You Can Access Applications¶
Visit http://demo.example.com
or https://demo.example.com
(depending on your TLS setup). The traffic flow is:
Browser → ALB (80/443) → NodePort on each K3s node → Traefik → Your Application
- If you offloaded TLS at the ALB, the ALB terminates HTTPS, then sends HTTP to Traefik on NodePort.
- If you are passing TLS through to Traefik, Traefik must have a certificate (e.g., from Let’s Encrypt or self-signed).
10. Scaling and Additional Management Nodes¶
If you add more K3s nodes (management or worker) that run the Traefik pods, you’ll need to register those node instances with each target group (HTTP and HTTPS). If you prefer an automatic approach, consider the AWS Load Balancer Controller which syncs cluster nodes with the ALB automatically.
Summary¶
- Keep Traefik as the in-cluster ingress controller.
- Create an ALB with two listeners (80 & 443).
- Forward traffic from each listener to NodePort target groups that map to Traefik’s NodePort range.
- Register all relevant node instance IDs so the ALB can distribute traffic across them.
- Create a DNS entry pointing your domain or subdomain at the ALB.
- Define Kubernetes
Ingress
objects that route to your services. - Test externally via HTTP/HTTPS.