Copyright (c) 2026 MindMesh Academy. All rights reserved. This content is proprietary and may not be reproduced or distributed without permission.

5.1.1. JavaScript, Docker, and Composite Actions

💡 First Principle: The three types differ in what the runner has to do before your code executes — run bundled JS on the preinstalled Node runtime, build or pull a container image, or simply inline your steps into the calling job — and that startup work is exactly the performance and portability trade-off.

DimensionJavaScriptDockerComposite
runs.usingnode20 (or node24)dockercomposite
Runner OSLinux, Windows, macOSLinux onlyAny (inherits caller)
Startup costFastest — runtime already presentSlowest — build or pull imageNone — steps inline
LanguageJavaScript/TypeScript (compiled to JS)Any language in the imageShell + other actions
IsolationRuns on the runner hostIsolated containerRuns on the runner host
Can set runs-on❌ inherits❌ inherits❌ inherits
Access to secrets contextVia inputs/envVia inputs/env⚠️ Must be passed as inputs
Typical useAPI calls, cross-platform toolingCustom toolchains, non-JS languagesBundling your own repeated steps

A JavaScript action points at a single entry file, and that file must be self-contained: the runner does not run npm install, so dependencies must be committed either as a checked-in node_modules or, far better, bundled with @vercel/ncc into one dist/index.js. It uses the @actions/core toolkit to read inputs and write outputs, and it can declare pre: and post: entry points that run before and after the main job step — the mechanism behind actions that set something up and reliably tear it down (actions/cache saves its cache in a post: step).

A Docker action names either an image (image: 'docker://alpine:3.19') or a local Dockerfile that GitHub builds at run time. Inputs arrive as INPUT_<NAME> environment variables, and args:/entrypoint: in the metadata override the image's defaults. The Linux-only restriction is absolute on hosted runners, and building the image per run is the reason these are slowest — pre-building and referencing a published image is the standard optimization.

A composite action lists steps: directly in action.yml. Every run: step must declare a shell:, and the steps execute in the caller's job on the caller's runner, sharing its filesystem. The catch is context access: secrets are not automatically available, so anything sensitive must be declared as an input and passed by the caller.

⚠️ Exam Trap: Docker container actions cannot run on Windows or macOS runners. If a scenario requires cross-platform support, the answer is JavaScript (or composite) — no amount of configuration makes a Docker action run on windows-latest.

Reflection Question: A JavaScript action's repository has a package.json with three dependencies and no dist/. It works locally and fails on the runner with "Cannot find module." Explain the mechanism, and name the two accepted fixes.

See how it connects
Alvin Varughese
Written byAlvin Varughese
Founder18 professional certifications