Safely Testing API Integrations Between Docker Compose Services in 7 Steps
Evidence and scope — Validation procedure based on official documentation
Official Docker and HTTP sources inform the integration-check sequence and example commands. This is not a completed validation report with a specific stack’s versions, responses, and execution logs; each step must be checked in the actual environment.
Having every container marked as running does not mean the API integration works. You still need to verify addressing and ports, readiness, authentication, read contracts, post-write retrieval, and test-data cleanup separately. Compose can manage when dependency containers start, but it does not automatically guarantee that their applications are ready to handle requests.[S4]
1. Choose the Address Based on Where the Request Originates
First, determine whether the request is coming from the host or from another container.
Containers on the same Compose network communicate using the service name and the container port.[S1]
http://api:8080Requests from the host to a container use the published host port.[S1]
http://localhost:18080For example, 18080:8080 maps port 18080 on the host to port 8080 in the container. Another container should call the api service at api:8080, not api:18080 or localhost:18080.[S1]
Check dynamically assigned host ports and potential conflicts as follows.[S1]
docker compose port api 8080If your setup involves network_mode: host, external networks, proxies, or separate Compose projects, do not rely on this basic rule alone. Inspect the actual network configuration.[S1]
2. Confirm When to Use host.docker.internal
host.docker.internal is not intended for communication between services on the same Compose network. Consider it when a container needs to access a process running on the host.
On Docker Desktop, this name resolves to the host’s internal IP address.[S2] Standard Docker Engine installations may not provide it automatically, so you may need a host-gateway mapping.[S3]
services: worker: extra_hosts: - "host.docker.internal:host-gateway"Verify this configuration with the Engine and Compose versions you actually use. With a remote Docker daemon, “host” may refer to the system running the daemon rather than your development machine.[S3]
Before making an API request, confirm name resolution and target-port connectivity from inside the calling container.[S2][S3]
3. Treat Running and Ready as Separate States
Start by inspecting the resolved Compose configuration and current service state.
docker compose configdocker compose psNext, use a healthcheck with depends_on.condition: service_healthy, or query a readiness endpoint directly from the calling container. With the service_healthy condition, Compose can wait for a dependency to pass its healthcheck.[S4]
A successful healthcheck or TCP connection still does not prove that:
- The correct token is being used
- The expected HTTP status code is returned
- The response schema matches the contract
- Create and delete operations work correctly
Use the target API specification to determine the actual path, success status codes, required JSON fields, error format, and token scope.
4. Keep Tokens Secret
Anyone who possesses a bearer token can use it, so protecting the raw value is essential.[S7] Never place tokens in URLs, Compose YAML, command output, logs, screenshots, or documentation.
Send tokens through the Authorization header, and show only variables in examples.
curl \ -H "Authorization: Bearer ${TOKEN}" \ -H "Accept: application/json" \ "http://api:8080/v1/resources/example"This approach can still expose the token if shell tracing is enabled or executed commands are recorded. Check your command-history and logging policies first.
Where supported for Linux containers, grant Compose secrets only to the services that need them and read each value from /run/secrets/<secret_name>.[S6]
services: worker: secrets: - api_tokensecrets: api_token: file: ./secrets/api_token.txtCompose secrets reduce exposure risk, but they do not automatically protect application request logs, error output, debug sessions, or shell history.[S6][S7] Do not treat a local Compose network itself as a guarantee of encryption in transit or strong isolation.
5. Validate the Read Contract Before Writing
Make the first API check with GET. GET is defined as a safe HTTP method, but that does not guarantee the absence of every side effect, such as implementation-specific logging or nonstandard state changes.[S5] Starting with GET reduces risk; it does not guarantee zero side effects.
Check each of the following separately:
- The expected status code
- The expected
Content-Type - The resource identifier and required response fields
- Whether authentication failure can be distinguished from a missing resource
- Whether error responses match the API contract
Do not stop at a single 200 OK. Validate the minimum contract in both the headers and response body. Once this step passes, you have evidence that addressing, authentication, and the read path work at least as expected.[S5]
6. Test Create, Retrieve, and Delete with a Dedicated Slug
Create a test-only slug that cannot be confused with existing data.
verify-<UTC시각>-<난수>This is a practical recommendation for reducing collisions and accidental deletion, not a standards requirement. Check the API specification for its actual slug restrictions and duplicate-handling behavior.
Use this sequence:
- Create one resource using the dedicated slug.
- Verify the response status and generated identifier.
- Retrieve the same slug with GET.
- Compare the stored values with the write request and read contract.
- Run the API’s documented deletion or recovery procedure.
- Retrieve it again to confirm that the resource is absent.
This sequence separates DNS and TCP connectivity, HTTP authentication, the read schema, persisted write results, and deletion into distinct checks. Create responses and post-deletion behavior vary between APIs, so do not hard-code assumptions such as 201 or 404.
7. Clean Up API Data Before the Compose Stack
Records created in an external data store may remain after docker compose down.[S8] Delete the test resource through the API first, then confirm that it is absent.
When the isolated test stack is no longer needed, remove its service containers and Compose network with the following command.[S8]
docker compose downBecause --volumes can remove declared named volumes and attached anonymous volumes, use it only after confirming that they contain disposable, test-only data.[S8]
docker compose down --volumesBind mounts, external volumes, and external data stores require separate cleanup.[S8]
Final Checklist
- Identified whether the caller is the host or a container.
- Used
service-name:container-portfor container-to-container communication.[S1] - Checked host-port conflicts and dynamic mappings with
docker compose port.[S1] - Determined whether
host.docker.internalwas appropriate and verified name resolution.[S2][S3] - Confirmed the healthcheck or readiness response.[S4]
- Kept the raw token out of URLs, output, logs, YAML, and documentation.[S6][S7]
- Used GET to validate authentication, the status code,
Content-Type, and required fields before writing.[S5] - Created a dedicated slug in the format
verify-<UTC-timestamp>-<random-value>. - Retrieved the same slug and compared the persisted result.
- Deleted the created resource through the API and confirmed that it was absent.
- Ran
docker compose down.[S8] - Used
--volumesonly when the data was explicitly intended to be discarded.[S8]
Sources
- [S1] Networking in Compose | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/compose/how-tos/networking/ ↩
- [S2] Explore networking how-tos on Docker Desktop | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/desktop/features/networking/networking-how-tos/ ↩
- [S3] dockerd | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/reference/cli/dockerd/ ↩
- [S4] Control startup and shutdown order in Compose | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/compose/how-tos/startup-order/ ↩
- [S5] RFC 9110: HTTP Semantics | IETF; R. Fielding, M. Nottingham, J. Reschke | 2022-06 | https://www.rfc-editor.org/rfc/rfc9110.html ↩
- [S6] Manage secrets securely in Docker Compose | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/compose/how-tos/use-secrets/ ↩
- [S7] RFC 6750: The OAuth 2.0 Authorization Framework: Bearer Token Usage | IETF; M. Jones, D. Hardt | 2012-10 | https://www.rfc-editor.org/info/rfc6750/ ↩
- [S8] docker compose down | Docker, Inc. | No publication or modification date shown on the page | https://docs.docker.com/reference/cli/docker/compose/down/ ↩
Report an error or share feedback
Open a draft with this article’s title and URL. Review the message and recipient before sending.
Open email draftIf no email app opens, copy these details into your usual email service.
Related posts
Backend & Infrastructure A Practical Roadmap to Becoming an Intermediate Spring Backend Developer
Follow a proposed path from Java and Spring fundamentals to APIs, databases, testing, security, and operations, measuring progress through practical capabilities.
Backend & Infrastructure Spring Boot 4.1 Highlights: gRPC, Jackson, and HTTP Address Filtering
An overview of Spring Boot 4.1’s gRPC support, Jackson read and write settings, and HTTP request address filtering, with Maven test AOT configuration changes to check before upgrading.