Kibana is the first service that depends on other services—it needs Elasticsearch running so that it can connect to the database. Docker Compose doesn't give any guarantees about the order in which it creates containers, so if you have a start-up dependency between services, you need to capture that in the service definition:
kibana:
image: sixeyed/kibana:5.6.11-windowsservercore-ltsc2019
labels:
- "traefik.frontend.rule=Host:kibana.nerddinner.local"
depends_on:
- elasticsearch
networks:
- nd-net
The depends_on attribute shows how to capture dependencies between services. In this case, Kibana is dependent on Elasticsearch, so Docker will ensure the elasticsearch service is up and running before starting the kibana service.
Kibana will be proxied by Traefik, but Traefik does not need to be running before Kibana. When Traefik starts, it gets a list of running containers from the Docker API to build its initial routing map. Then it subscribes to the event stream from Docker to update the routing rules when containers are created or removed. So, Traefik can start before or after the web containers.
Containers for the kibana service also connect to the application network. In an alternative configuration, I could have separate backend and frontend networks. All the infrastructure services would connect to the backend network, and the public-facing services would connect to the backend and frontend networks. These are both Docker networks, but separating them would give me the flexibility to configure the networks differently.