Skip to content

node ace does not exit in Docker when the app is bind-mounted from Windows/WSL #166

@jakeb-k

Description

@jakeb-k

Package version

@adonisjs/core@6.18.0 @adonisjs/ace@13.3.0

Describe the bug

node ace does not exit in Docker when the app is bind-mounted from Windows/WSL

I isolated this to a minimal reproduction repo with a Dockerfile and exact commands:

https://github.com/jakeb-k/node-ace-self-exit

The reproduction includes:

  • a clean Adonis starter app
  • the Docker config used to reproduce it
  • exact commands and observed exit codes
  • a toggleable local workaround showing the difference between normal behavior and a forced exit after command completion

Summary

When running node ace inside a Linux Docker container against an app bind-mounted from a Windows/WSL working tree, some non-staysAlive commands do not terminate on their own after the command work is done.

In the repro:

  • node ace list can take much longer than expected to exit
  • node ace test stays alive until killed by timeout
  • the same app exits normally when /app/node_modules comes from a Docker-managed volume instead of the bind-mounted working tree

This is a blocker for autonomous agent workflows such as Claude/Codex, because they wait for the process to exit before proceeding.

Expected behavior

Non-staysAlive Ace commands should terminate once the command has completed and Adonis has finished shutdown.

Actual behavior

In the repro environment, node ace test does not terminate on its own.

Example from the repro:

docker run --rm \
  -e APP_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
  -e HOST=0.0.0.0 \
  -e LOG_LEVEL=info \
  -e PORT=3333 \
  -v 'C:\Users\jk_we\Desktop\apps\node-ace-self-exit:/app' \
  -w /app \
  node:22-bookworm-slim \
  timeout 60s node ace test

Observed result:

[ info ] booting application to run tests...

 NO TESTS EXECUTED

Observed exit code:

124

Reproduction repo

Repo:

https://github.com/jakeb-k/node-ace-self-exit

Files of interest:

  • REPRO.md
  • Dockerfile
  • docker-compose.yml
  • bin/console.ts

REPRO.md contains the full environment details, exact commands, and the active-handle dump.

What I narrowed down

The repro suggests this is related to the loader path used by the starter:

import 'ts-node-maintained/register/esm'
await import('./bin/console.js')

In the repro, importing ts-node-maintained/register/esm inside the bind-mounted environment is enough to produce the problematic behavior pattern.

I also captured active handles and saw a ref'd MessagePort in the failing case that is absent when /app/node_modules comes from a Docker volume.

I am not claiming the root cause is necessarily inside Ace/Core itself. The point of the issue is:

  1. there is a reproducible environment where node ace ... does not terminate reliably for non-staysAlive commands
  2. this is user-visible and blocks tooling/automation workflows
  3. the repro is isolated enough to inspect with a Dockerfile and exact commands

Workaround included in the repro

The repro app also includes a local opt-in workaround in bin/console.ts:

  • --force-exit
  • --no-force-exit
  • ADONIS_ACE_FORCE_EXIT=true

With that toggle enabled, the same node ace test command exits 0 instead of timing out.

I am not opening this as “Ace should always call process.exit()”. I am opening it because there is a reproducible non-exit scenario, and the workaround demonstrates that an opt-in forced exit after normal shutdown resolves the operational problem for CI/agent tooling.

If this issue is confirmed, I can follow up with a PR against adonisjs/core for an opt-in force-exit path after normal command shutdown.

Reproduction repo

https://github.com/jakeb-k/node-ace-self-exit

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions