Skip to content

Custom Actions Overview


You’ve now built a composite action, and you’ve used dozens of third-party actions without ever asking what’s actually running underneath uses:. It’s worth pulling back the curtain for a moment before moving on, because “composite” is only one of three ways an action can be built — and knowing what the other two are is what lets you recognize, next time you’re evaluating some action on the Marketplace, roughly what you’re pulling into your workflow.

The Three Kinds of Action

Every action you uses: is one of these under the hood, declared by the runs.using: key in its action.yml:

ScopeCompositeJavaScriptDocker
What it actually isA YAML file listing steps to runA Node.js script, bundled with its action.ymlA Dockerfile plus an entrypoint, packaged as a container image
Runtime it needsWhatever its own steps need — nothing extraNode.js (already installed on every GitHub-hosted runner)Whatever the image contains — any language, any OS package, entirely self-contained
Startup costNone — its steps run directly on the runner, same as your ownLow — no image to build or pull, just a script to executeHigher — the image has to be built or pulled before the action can run at all
What you’ve seen it doPrevious chapter ssl-cert-monitoractions/checkout, actions/setup-python, and most other third-party actions you’ve used all courseNot yet — You may see later in the course
Best fitGluing together a handful of steps or existing actions, with light shell logic in betweenReal custom logic beyond what shell scripting handles comfortably, fast execution, works the same on Linux/macOS/Windows runnersThe action needs a specific OS environment, a non-JS toolchain, or system packages a runner doesn’t ship with

Composite is the one you already know how to build, and it covers a lot of ground: if the logic you need can be expressed as “run these steps, in this order, with these inputs,” you don’t need anything fancier. That’s exactly the situation ssl-cert-monitor was in — Python and a shell script were already enough.

JavaScript Actions, Conceptually

Almost every action you’ve referenced this course — actions/checkout, actions/setup-python, astral-sh/ruff-action — is a JavaScript action. That’s not a coincidence: JS actions start fast (no container to spin up), run identically across GitHub’s Linux, macOS, and Windows runners, and give the author a real programming language when run: steps and shell scripting stop being enough — parsing structured API responses, calling GitHub’s REST API directly through the @actions/toolkit packages, or handling logic that’s awkward to express as bash.

The tradeoff is the one built into any Node.js project: dependencies need to be bundled into the repo (usually via a build step, since the action runs exactly as committed — no npm install happens at execution time), and writing one means knowing JavaScript or TypeScript. That’s genuinely out of scope here — this course teaches CI/CD, not a programming language, and everything ssl-cert-monitor needs to do is already comfortably composite. You now know when you’d reach for a JS action instead: composite action logic starting to feel like it’s fighting the shell, or wanting the exact same custom logic to run identically across Linux, macOS, and Windows runners without three separate shell dialects.

Docker Actions, Coming Later (Maybe)

Docker actions solve a different problem entirely: what if the tool you need to run isn’t Python, isn’t Node.js, and isn’t something a GitHub-hosted runner has preinstalled at all? A Docker action packages its own environment — any base image, any system packages, any language’s runtime — so the action carries its dependencies with it instead of assuming the runner already has them.

You haven’t touched Docker in this course yet, so building one isn’t happening here — we may cover in later sections if possible. Once containers themselves are covered, Docker actions will be the natural next step, and composite plus (eventually) Docker actions cover the overwhelming majority of real actions you’ll ever need to write. JavaScript sits between them: capable of more than composite, but not something this course is going to teach you to write from scratch.

Where This Leaves You

For ssl-cert-monitor, composite was never really in competition with the other two — it’s Python and a shell command, nothing a container or a compiled JS bundle would do any better. The next chapter takes that same composite action and gets it ready to publish — real semantic versioning, a real Marketplace listing, the same kind of thing every third-party action you’ve pinned to a version tag this whole course has already been through.

Last updated on