Skip to content

tailwindcss:watch exits immediately after build in Docker environment (v4.4.0) #602

@dapi

Description

@dapi

Environment

  • tailwindcss-rails: 4.4.0
  • tailwindcss-ruby: 4.1.16
  • Ruby: 3.4.2
  • Rails: 8.1.1
  • Docker: Debian-based container
  • Watchman: v2024.11.04.00 (installed and working)
  • Process manager: Foreman via Procfile.dev

Problem

bin/rails tailwindcss:watch exits immediately after initial build with exit code 0, instead of staying in watch mode.

Procfile.dev

web: bin/rails server
css: bin/rails tailwindcss:watch

Output

14:37:19 css.1  | ≈ tailwindcss v4.1.16
14:37:20 css.1  | Done in 314ms
14:37:20 css.1  | exited with code 0
14:37:20 system | sending SIGTERM to all processes

The CSS process exits immediately after "Done", which triggers foreman to send SIGTERM to all other processes.

Investigation

1. Watchman is installed and working

$ watchman version
version: 20251222.073500.0
buildinfo: v2024.11.04.00

$ watchman watch-list
{
    "version": "20251222.073500.0",
    "roots": [
        "/app"
    ]
}

2. Bug in watch_command method

In lib/tasks/build.rake:

def watch_command(always: false, **kwargs)
  compile_command(**kwargs).tap do |command|
    command << "-w"
    command << "always" if always  # This produces: -w always (two separate args)
  end
end

This generates -w always as two separate arguments, but the tailwindcss CLI expects --watch=always or --watch always as a single flag with value.

3. Even with correct flag, tailwindcss exits

Tried running directly:

$ bundle exec tailwindcss --input app/assets/stylesheets/application.css --output app/assets/builds/application.css --watch=always
≈ tailwindcss v4.1.16

Done in 455ms
$ # Process exits immediately

Expected Behavior

The tailwindcss:watch task should stay running and rebuild CSS when source files change.

Workaround Attempted

  • Passing always argument: css: bin/rails tailwindcss:watch[always] - doesn't work
  • Direct tailwindcss command with --watch=always - exits immediately
  • FIFO stdin wrapper to keep stdin open - exits immediately

Possible Causes

  1. The watch_command method incorrectly constructs the watch flag
  2. Tailwindcss v4 may have different stdin/tty requirements for watch mode
  3. In Docker/non-interactive environment, tailwindcss may need additional flags

Suggested Fix

At minimum, fix the watch_command to properly pass the flag:

def watch_command(always: false, **kwargs)
  compile_command(**kwargs).tap do |command|
    command << (always ? "--watch=always" : "--watch")
  end
end

But there may be a deeper issue with tailwindcss v4 watch mode in non-interactive environments.

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