Skip to content

add the ability to set aliases per network - #27

Merged
obeone merged 6 commits into
obeone:mainfrom
egolus:network_alias
Jun 21, 2026
Merged

add the ability to set aliases per network#27
obeone merged 6 commits into
obeone:mainfrom
egolus:network_alias

Conversation

@egolus

@egolus egolus commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

We have the problem that we want to isolate our backend services but they sometimes still have to talk to each other. (i.e. a webserver may need to send e-mails through a mail server)
Normally a backend container can't reach the reverse proxy by the public domain names. So with this patch we give the reverse proxy those needed public domain names as alias so one container can reach one in another stack through the reverse proxy.

an example compose file would look something like this:

services:
  wordpress:
    image: wordpress
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: <user>
      WORDPRESS_DB_PASSWORD: <password>
      WORDPRESS_DB_NAME: <dbname>
    volumes:
      - wordpress:/var/www/html
    networks:
      - default
      - traefik
    labels:
      - traefik.enable=true
      - traefik.docker.network=web_traefik
      - traefik.http.routers.web.rule=Host(`domain.test`)
      - traefik.http.routers.web.entrypoints=websecure
      - traefik.http.routers.web.tls.certresolver=myresolver
      - traefik.aliases=mail.domain.test
  db:
    image: mariadb
    environment:
      MARIADB_DATABASE: <dbname>
      MARIADB_USER: <user>
      MARIADB_PASSWORD: <password>
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:

networks:
  default:
  traefik:

traefik.aliases can be a comma-separated string. If there are multiple containers in a stack, traefik.aliases should only appear once as it may be overwritten

@obeone obeone left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @egolus, thanks for this PR — the use case is clearly valid and the implementation is clean overall. A few things to address before merging:

Bug: aliases applied to all networks
The aliases are currently applied to every network the container is connected to. They should ideally be scoped to the relevant network(s) only, otherwise Traefik may receive the same aliases on unrelated networks, which could cause conflicts.

Code consistency
The rest of the codebase uses container.labels (the idiomatic Docker SDK API). Please replace container.attrs["Config"]["Labels"] with it:

alias_label = container.labels.get("traefik.aliases", "")
aliases = [a.strip() for a in alias_label.split(",") if a.strip()] if alias_label else []

Tests
The project has a tests/ directory — please add unit tests covering at least: no alias, single alias, multiple comma-separated aliases, and aliases with extra whitespace.

Documentation
Please document the new traefik.aliases label in the README (alongside the existing label documentation).


Optionally, if you want to go further: making the label name configurable via config.traefik.aliasLabel would follow the existing pattern — but that's a nice-to-have, not a blocker.

@egolus

egolus commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Sorry for waiting so long. Can you take a look if the fixes are ok?

@obeone obeone left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thanks for this! The per network alias support is genuinely handy and the tests are nice and readable. I checked out the branch, ran it, and the whole suite is green.

I did run into one small edge case that I think is worth fixing before merge.

The aliases only get applied when the network is explicitly listed in the allowed networks label. But if a container does not set that label at all, allowed_networks ends up as [''], so the outer guard still connects Traefik (which is what we want), yet the inner net in allowed_networks check is False and the aliases get dropped silently. So someone who just sets traefik.aliases without also listing allowed networks gets no alias and no error, which is a bit surprising.

Since that block already lives inside the if allowed_networks == [''] or net in allowed_networks: guard, the extra check is redundant and we can drop it. I left a one line suggestion on the relevant line.

I would also add a small regression test for the no label case so it does not creep back in later:

    def test_alias_set_without_network_label(self, mock_docker_client, mock_config, mock_logger):
        """
        Regression: when traefik.aliases is set but no allowed-networks label is
        provided (all networks allowed by default), the aliases must still be
        applied to the network connect call.
        """
        container = MagicMock()
        container.name = "web-app"
        container.attrs = {
            "NetworkSettings": {"Networks": {"app_net": {}}}
        }
        container.labels = {
            "traefik.aliases": "app",
        }

        traefik = MagicMock()
        traefik.attrs = {"NetworkSettings": {"Networks": {"bridge": {}}}}
        mock_docker_client.containers.get.return_value = traefik

        network = MagicMock()
        network.attrs = {"Name": "app_net", "Labels": {}}
        mock_docker_client.networks.get.return_value = network

        main.connect_traefik_to_network(container)

        network.connect.assert_called_once_with(traefik, aliases=["app"])

With both changes the full suite stays green (62 passing on my side). Thanks again for the contribution!

Comment thread main.py Outdated
@obeone obeone closed this Jun 20, 2026
@obeone obeone reopened this Jun 20, 2026
obeone added 2 commits June 21, 2026 02:08
The alias block lived inside the allowed-networks guard but added a
redundant "net in allowed_networks" check. In the default case where no
allowed-networks label is provided (allowed_networks == ['']), that check
was False, so aliases were silently dropped even though Traefik was still
connected. Drop the redundant check so aliases apply whenever they are set.

Add a regression test covering the no-label case.
Align the workflow with the base branch so the Claude review can access
secrets and an OIDC token on pull requests opened from forks, where the
plain pull_request trigger runs without them.
@obeone

obeone commented Jun 21, 2026

Copy link
Copy Markdown
Owner

@claude please review the two latest commits on this PR (the alias fix and its regression test). Flag anything blocking before I merge.

@claude

claude Bot commented Jun 21, 2026

Copy link
Copy Markdown

Claude finished @obeone's task in 1m 21s —— View job


Review: alias fix + regression test

  • Read the two latest commits (f423903, 4f41a0f)
  • Review main.py alias fix
  • Review regression test
  • Flag any blockers

No blockers. These two commits are clean and correct. Here's the breakdown:

main.py — alias fix (main.py:142)

The old guard was:

if aliases and net in allowed_networks:

When no network label is set, allowed_networks is [''], so net in allowed_networks is always False for any real network name — aliases silently dropped.

The new guard is simply:

if aliases:

This is correct: we're already inside if allowed_networks == [''] or net in allowed_networks:, so we know this network is allowed. The extra membership check was redundant and harmful in the default (no-label) case. Fix is accurate.

Regression test — test_alias_set_without_network_label

The test faithfully reproduces the failure mode:

  • Container has traefik.aliases: "app" but no traefik.docker.network label
  • Verifies network.connect(traefik, aliases=["app"]) is called

Mock setup is consistent with all other tests in the class. Logic path through the function with these inputs:

  1. allowed_networks = [''] (label absent → empty string split)
  2. outer guard: [''] == ['']True
  3. if aliases:True, so network.connect(traefik, aliases=["app"])

Assertion will pass.

Minor non-blocking note

The 4-line block comment added above the if aliases: line (main.py:137-141) is somewhat verbose for the project style (CLAUDE.md says one short line max). Not a blocker — the explanation is genuinely useful here — but worth trimming in a follow-up if you care about stylistic consistency.


Verdict: safe to merge. The fix is minimal and precise, the regression test covers the exact reported edge case, and the suite (62 tests per your comment) stays green.

@obeone
obeone merged commit aa5676e into obeone:main Jun 21, 2026
2 checks passed
@obeone

obeone commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Hey @egolus, thanks a lot for this. The per-network alias support is a genuinely nice addition, and your tests were a pleasure to read.

Quick apology for the noise on this PR. Most of the red CI runs and the open/close dance you saw came from my side: I was wiring up the Claude code review GitHub app on the repo at the same time, and figuring out how it behaves on pull requests opened from forks (turns out a fork PR cannot reach the secrets it needs unless the workflow runs as pull_request_target). None of that was about your code.

While I was in there, I pushed two small commits onto your branch: the alias fix from my review (apply the aliases even when no allowed-networks label is set, plus a regression test for that case), and a tweak to the review workflow so it stops failing on fork PRs. The suite stays green, so I went ahead and merged.

Thanks again for the contribution, and sorry for making you sit through a CI light show in the meantime.

@egolus

egolus commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

no worries. Thanks for the last fixes and merging 👍

@egolus
egolus deleted the network_alias branch June 21, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants