Forge Fate
Backend & Infrastructure

Safely Testing API Integrations Between Docker Compose Services in 7 Steps

5 min read

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]

text
http://api:8080

Requests from the host to a container use the published host port.[S1]

text
http://localhost:18080

For 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]

bash
docker compose port api 8080

If 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]

yaml
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.

bash
docker compose configdocker compose ps

Next, 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.

bash
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]

yaml
services:  worker:    secrets:      - api_tokensecrets:  api_token:    file: ./secrets/api_token.txt

Compose 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.

text
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:

  1. Create one resource using the dedicated slug.
  2. Verify the response status and generated identifier.
  3. Retrieve the same slug with GET.
  4. Compare the stored values with the write request and read contract.
  5. Run the API’s documented deletion or recovery procedure.
  6. 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]

bash
docker compose down

Because --volumes can remove declared named volumes and attached anonymous volumes, use it only after confirming that they contain disposable, test-only data.[S8]

bash
docker compose down --volumes

Bind 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-port for container-to-container communication.[S1]
  • Checked host-port conflicts and dynamic mappings with docker compose port.[S1]
  • Determined whether host.docker.internal was 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 --volumes only when the data was explicitly intended to be discarded.[S8]

Sources

Report an error or share feedback

Open a draft with this article’s title and URL. Review the message and recipient before sending.

To: [email protected]

Open email draft

If no email app opens, copy these details into your usual email service.

Contact information