Amazon EC2
This feature is currently in early access.
The ec2 provider enables Traefik Hub API Gateway to discover Amazon EC2 instances
and automatically generate routing configuration from their tags. It follows the same label-based model as the
Docker, ECS, and Consul Catalog providers: you apply traefik.*
tags to your EC2 instances to define routers, services, middlewares, and load balancer settings, and the provider
turns them into dynamic configuration.
This is useful for workloads running directly on EC2 instances, without a container orchestrator, that still need automatic service discovery and routing. As instances launch, terminate, or change addresses, the provider keeps the routing configuration in sync.
Overview
The provider polls the AWS EC2 API at a configurable interval to list running instances, filters them using user-defined EC2 filters and constraint expressions, and builds Traefik routing configuration from each instance's tags. HTTP, TCP, and UDP services are supported.
Key behaviors:
- Tag-based configuration: Instances are configured through
traefik.*tags, mirroring the Docker and Consul Catalog providers. - Running instances only: The provider always scopes discovery to instances in the
runningstate. This is an AWS-side filter, not a health check; see Health checking. - Backend addressing: Backends target the instance's private IP by default, with public and IPv6 modes available. See ipMode.
- Service merging: The service name is derived from each instance's
Nametag. Instances sharing the same name are merged as servers behind the same load balancer. - Flexible credentials: Use static AWS credentials or the default AWS credential chain (IAM roles, environment variables, instance profiles). See Credentials.
- Optional port discovery: When no port tag is set, the backend port can be derived from the instance's security-group rules. See securityGroupPortDiscovery.
- Cross-provider reference: Reference discovered resources from other providers by suffixing the name with
@ec2, e.g. a Kubernetes gateway routing to an EC2 backend. See Provider Namespace.
Configuration Example
To use the ec2 provider, you need to enable & configure it.
Below is an example of how to enable & configure the provider:
- Helm Chart
- Install Configuration
hub:
providers:
ec2:
enabled: true
region: "us-east-1"
exposedByDefault: false
refreshSeconds: 15
filters:
- name: "tag:env"
values:
- "production"
additionalArguments:
- "--hub.providers.ec2.region=us-east-1"
- "--hub.providers.ec2.exposedByDefault=false"
- "--hub.providers.ec2.refreshSeconds=15"
- "--hub.providers.ec2.filters[0].name=tag:env"
- "--hub.providers.ec2.filters[0].values=production"
hub:
providers:
ec2:
region: "us-east-1"
exposedByDefault: false
refreshSeconds: 15
filters:
- name: "tag:env"
values:
- "production"
Configuration Options
| Field | Description | Default | Required |
|---|---|---|---|
hub.providers.ec2.region | AWS region used for EC2 API requests. When empty, the region is retrieved from the EC2 Instance Metadata Service. | "" | No |
hub.providers.ec2.accessKeyID | AWS access key ID used for requests. When set together with secretAccessKey, these static credentials take priority over the default credential chain. See Credentials. | "" | No |
hub.providers.ec2.secretAccessKey | AWS secret access key used for requests. Must be set together with accessKeyID. See Credentials. | "" | No |
hub.providers.ec2.exposedByDefault | Expose discovered instances by default. When set to false, only instances with the traefik.enable=true tag are exposed. See exposedByDefault. | true | No |
hub.providers.ec2.refreshSeconds | Polling interval, in seconds, for the EC2 API. | 15 | No |
hub.providers.ec2.defaultRule | Default routing rule applied to instances that do not define a router rule tag. See defaultRule. | "Host(`{{ normalize .Name }}`)" | No |
hub.providers.ec2.constraints | Expression matched against instance tags to determine whether to create routes for an instance. See constraints. | "" | No |
hub.providers.ec2.ipMode | Default backend IP mode: private, public, or ipv6. Can be overridden per instance with the traefik.ec2.ipmode tag. See ipMode. | private | No |
hub.providers.ec2.filters | List of EC2 API filters used to scope instance discovery. See filters. | [] | No |
hub.providers.ec2.filters[n].name | EC2 filter name (for example tag:env, vpc-id, instance-type). | "" | No |
hub.providers.ec2.filters[n].values | List of values for the filter. | [] | No |
hub.providers.ec2.securityGroupPortDiscovery | Enable deriving the backend port from an instance's security-group rules when no port tag is set. See securityGroupPortDiscovery. | N/A | No |
hub.providers.ec2.securityGroupPortDiscovery.excludedPorts | Ports excluded from security-group port discovery. When empty, the provider excludes port 0 and all privileged ports (below 1024) except 80 and 443. | Privileged ports except 80 and 443 | No |
region
The AWS region used for EC2 API requests. If region is not set, the provider retrieves the region from the
EC2 Instance Metadata Service,
which requires Traefik Hub to run on an EC2 instance.
Credentials
The provider authenticates against the AWS EC2 API in one of two ways:
- Static credentials: When both
accessKeyIDandsecretAccessKeyare set, the provider uses only these credentials, and they take priority over any other source. - Default credential chain: When
accessKeyIDandsecretAccessKeyare not set, the provider falls back to the default AWS credential chain, which resolves credentials from environment variables, shared configuration files, IAM roles, and EC2 instance profiles.
For workloads running on AWS, prefer an IAM role
or instance profile over static credentials. The role needs permission to call ec2:DescribeInstances
(and ec2:DescribeSecurityGroups when securityGroupPortDiscovery is enabled).
accessKeyID and secretAccessKey are sensitive values and are not logged.
exposedByDefault
When exposedByDefault is true (the default), every discovered instance is exposed unless it carries the
traefik.enable=false tag. When set to false, instances are ignored unless they carry the traefik.enable=true tag.
defaultRule
The default routing rule applied to instances that do not define a router rule through a tag.
For a given instance, if no routing rule is defined by a tag, the defaultRule is used instead.
The defaultRule must be a valid Go template,
and can include sprig template functions.
The instance name (derived from the Name tag) can be accessed with the Name identifier.
The default value is:
Host(`{{ normalize .Name }}`)
The rule can be overridden on an instance basis with the traefik.http.routers.{name-of-your-choice}.rule tag.
Set defaultRule to an empty string to stop the provider from creating a route for instances that don't define
their own router rule tag. This is useful when you only want an EC2 instance to be discoverable as a service —
for example to reference it from another provider or cluster with the @ec2 suffix (see
Provider Namespace) — without a route being created for it by default.
constraints
The constraints option can be set to an expression that Traefik Hub matches against an instance's tags to determine
whether to create any route for that instance. If the tags do not match the expression, no route is created for that
instance. If the expression is empty, all detected instances are included.
The expression syntax is based on the Label(`key`, `value`) and LabelRegex(`key`, `regex`) functions,
as well as the usual boolean logic, as shown in the examples below.
Constraints Expression Examples
# Includes only instances having the tag `a.tag.name=foo`
constraints: "Label(`a.tag.name`, `foo`)"
# Excludes instances having the tag `a.tag.name=foo`
constraints: "!Label(`a.tag.name`, `foo`)"
# With logical AND.
constraints: "Label(`env`, `production`) && Label(`tier`, `web`)"
# With logical OR.
constraints: "Label(`env`, `production`) || Label(`env`, `staging`)"
# With logical AND and OR, with precedence set by parentheses.
constraints: "Label(`env`, `production`) && (Label(`tier`, `web`) || Label(`tier`, `api`))"
# Includes only instances whose `env` tag value matches the `prod-.+` regular expression.
constraints: "LabelRegex(`env`, `prod-.+`)"
# Includes only instances that carry the `env` tag at all, whatever its value.
# Use `.+` rather than `.*`: `.*` also matches instances that do not have the tag.
constraints: "LabelRegex(`env`, `.+`)"
filters
The filters option scopes instance discovery using the EC2 DescribeInstances filters.
Each filter has a name and a list of values. Only instances matching every configured filter are considered.
- Install Configuration
hub:
providers:
ec2:
filters:
- name: "vpc-id"
values:
- "vpc-0123456789abcdef0"
- name: "tag:env"
values:
- "production"
- "staging"
The provider always scopes discovery to running instances by injecting the instance-state-name=running filter.
This filter is reserved: configuring a filter named instance-state-name causes the provider to fail at startup.
Traefik continues to run without the EC2 provider and logs the reason at ERROR.
This is a first-layer, AWS-side check only; it does not verify that the backend on the instance is actually healthy.
See Health checking to add an application-level check on top of it.
Health checking
The running-state filter above only confirms that AWS considers the instance to be running — it is not an
application-level health check. To validate that a backend is actually accepting traffic before routing to it,
configure a health check through tags, for example:
traefik.http.services.{name-of-your-choice}.loadbalancer.healthcheck.path=/health
traefik.http.services.{name-of-your-choice}.loadbalancer.healthcheck.interval=10s
Health checking is optional and layered on top of the running-state filter: an instance can be in the running
state and still be excluded from load balancing if its configured health check fails.
ipMode
The ipMode option selects which address of an instance the backends should target:
private(default): the instance's private IPv4 address.public: the instance's public IPv4 address.ipv6: the instance's IPv6 address.
The mode can be overridden on a per-instance basis with the traefik.ec2.ipmode tag. Instances without an address
for the resolved mode are skipped.
The private default only works when the Hub gateway itself runs inside the instance's VPC or a peered network.
A gateway hosted outside AWS — or outside that VPC — cannot reach private addresses at all, so on that topology
the default silently produces unroutable backends. Set ipMode to public (or override per instance with the
traefik.ec2.ipmode tag) when the gateway doesn't run inside AWS.
securityGroupPortDiscovery
When no port is defined for an instance through a tag, the provider can derive the backend port from the instance's
security-group rules. This is opt-in: set securityGroupPortDiscovery to enable it.
When enabled, the provider inspects the inbound rules of the security groups attached to the instance and uses the
lowest FromPort for the relevant protocol (TCP or UDP), skipping any port listed in excludedPorts.
When excludedPorts is not set, the provider skips port 0 and all privileged ports (below 1024) except 80 and
443. Ports at or above 1024 are eligible, so an instance whose security group exposes an application or database
port will be routed to it unless you exclude that port explicitly.
Setting excludedPorts replaces this rule rather than adding to it: only the ports you list are skipped, and
the privileged-port range no longer applies.
- Install Configuration
hub:
providers:
ec2:
securityGroupPortDiscovery:
excludedPorts:
- 8080
excludedPorts overrides, not extends, the defaultThe example above excludes only port 8080. Setting excludedPorts replaces the default privileged-port range
entirely rather than adding to it, so every other privileged port — including 22, 25, and 3389 — becomes
eligible for port discovery once excludedPorts is set.
Security-group port discovery only applies when an instance does not set an explicit port tag.
When enabled, the provider also needs the ec2:DescribeSecurityGroups permission.
Routing Configuration with Tags
The provider reads traefik.* tags from each EC2 instance and decodes them into routers, services, and middlewares,
exactly like the label-based providers. Provider-specific options use the traefik.ec2. prefix. Few examples:
| Tag | Description |
|---|---|
traefik.enable | Whether to expose the instance. Interacts with exposedByDefault. |
traefik.ec2.ipmode | Overrides the provider-level ipMode for this instance (private, public, or ipv6). |
traefik.http.routers.<name>.rule | Defines the routing rule for an HTTP router, overriding the defaultRule. |
traefik.http.services.<name>.loadbalancer.server.port | Sets the backend port for an HTTP service. |
traefik.tcp.routers.<name>.rule | Defines the routing rule for a TCP router. |
traefik.udp.routers.<name>.entrypoints | Assigns entry points for a UDP router. |
The full set of routing tags follows the same naming as the standard Traefik label-based providers. See the
Docker provider labels
documentation for the complete label/tag syntax reference — for example,
traefik.http.middlewares.<name>.<middleware-type>.<option> to configure middlewares, or
traefik.http.services.<name>.loadbalancer.server.scheme to set the backend scheme.
- Example EC2 Instance Tags
traefik.enable=true
traefik.http.routers.my-app.rule=Host(`my-app.example.com`)
traefik.http.services.my-app.loadbalancer.server.port=8080
With these tags, and assuming the instance has the Name tag my-app, the provider creates an HTTP router matching
Host(my-app.example.com) and a service whose backend targets the instance's private IP on port 8080.
When a service does not set an explicit port tag, the port is resolved through securityGroupPortDiscovery
if enabled; otherwise the service is ignored. HTTP services default to the http scheme unless a scheme or a full
server URL is set through tags.
Provider Namespace
Resources discovered by the ec2 provider are created in the ec2 provider namespace.
When referencing such a resource from another provider, suffix its name with @ec2:
<resource-name>@ec2
For more details on provider namespaces, see the providers overview.
Additional Notes
- The service name is derived from each instance's
Nametag, normalized. Multiple instances sharing the sameNameare merged as servers behind the same load balancer. - Only instances in the
runningstate are discovered. - Backends use the address selected by ipMode; instances without an address for the resolved mode are skipped.
- The provider retries unsuccessful AWS API calls with exponential backoff and retains the last valid configuration until the next successful refresh.
