Note (2026): The ambassador pattern is outdated. Docker 1.9+ (2015) introduced user-defined networks, which solve this problem more elegantly. Containers on the same network see each other by service name without links. This article is preserved as a historical artifact; the problem described here is no longer current.
When designing an information system, clarity is very important. There is nothing better than an elegantly built application with no unnecessary details, including unnecessary links between components.
Fortunately, Docker lets us isolate processes from each other and organize communication between them by means of predeclared ports and shared directories. In most cases, only a one-way connection between containers is needed for the whole thing to succeed, for example: a web server uses php-fpm, or php-fpm uses a database. In this simple case, no problems arise; establishing such a connection between several containers using Docker Compose is very easy:
docker-compose -f test.yml up -dCreating testapp_fpm_1...Creating testapp_nginx_1...docker-compose ps Name Command State Ports-------------------------------------------------------------------------testapp_fpm_1 /usr/sbin/php5-fpm -F Up 9000/tcptestapp_nginx_1 /usr/sbin/nginx Up 80/tcp
From the testapp_nginx_1 container, testapp_fpm_1 can be addressed through the hosts written by Docker itself. Let us look at /etc/hosts:
In principle, a situation is possible where both linked processes must know about each other. For example, if Selenium is running: it needs access to the web server in order to execute test tasks; but the web server also needs access to Selenium itself in order to start those tasks through a BDD framework. For clarity of the example relative to the previous one, let us suppose that we need access from nginx to php-fpm and back.
docker-compose -f test2.yml up -dCircular import between fpm and nginx
Fortunately, there is a solution. It is impossible to add cross-links only at the startup stage, but no one can forbid adding them after startup. Ambassador comes to the rescue: a proxy container that forwards connections to the target container, so both sides can link to it instead of to each other.
As you can see, Docker added the standard records for ambassador, and then ambassador added a record pointing to the fpm container. It is worth noting: not directly, but through that same ambassador.