A 3x Throughput Jump for GitHub Actions on Enterprise Server

GitHub Actions has been generally available on GitHub Enterprise Server (GHES) since the 3.0 release roughly two years ago. Since then, a series of performance engineering efforts have significantly reduced the CPU load GitHub Actions places on the server, unlocking far greater concurrency. On 96-core machines, the maximum number of concurrent jobs has climbed from 2,200 on GHES 3.2 to 7,000 on the current GHES 3.6 release—a threefold improvement.

The path to that milestone involved revisiting some fundamental assumptions about how the system was built and finding wins in unexpected places.

Enabling a Cache That Was Never On

The first major discovery came from profiling the hottest code path used to access workflow secrets and callback URLs. The team assumed this path was already leveraging a cache, but analysis of profile traces from load testing and production revealed it was not. What made this particularly tricky was that the team had extensive monitors for every new cache added to the product—this particular one had simply been presumed active for years.

Enabling the cache was a single-line change that produced immediate results: CPU usage dropped sharply, workflow execution sped up, and overall throughput increased. The lesson here is that sometimes the biggest performance wins don’t require deep architectural surgery.

Re-Architecting Orchestration State Storage

The Old Approach: One Large Blob

GitHub Actions relies on an orchestration layer—a durable state machine—to keep workflow runs resilient against machine shutdowns and intermittent failures. For durability, every time the orchestrator wakes up, it replays execution from the beginning to rebuild local state until it either finishes or encounters new work.

The problem was in how that state was persisted. Orchestration state was stored in a single database column as one large blob of events. Updating the state meant reading the entire blob into memory, appending new events, and writing the whole thing back. This caused unnecessary overhead: a growing blob was constantly deleted and rewritten with a nearly identical, slightly larger value. Replaying state required reading and deserializing the entire blob each time.

CREATE TABLE tbl_OrchestrationSession (
   SessionId           BIGINT              NOT NULL,
   CreatedOn           DATETIME            NOT NULL DEFAULT GETUTCDATE(),
   LastUpdatedOn       DATETIME            NOT NULL DEFAULT GETUTCDATE(),
   ...
   State               VARBINARY(MAX)      NULL, -- this is where we store execution state
)

The New Approach: Incremental Reads and Writes

The orchestration system was rebuilt to support both incremental reads and incremental writes. State history now lives in its own dedicated table rather than as an inline binary blob. When state is updated, only the new events are written to the database. This design also enables smarter caching, where the system can skip fetching all historical events and only pull pending events. Replay overhead is largely eliminated, making long-running orchestrations with many workflow steps far less costly.

-- new table to store execution state
CREATE TABLE tbl_OrchestrationSessionEvent (
   ...
   SessionId       BIGINT          NOT NULL,
   EventId         BINARY(20)      NOT NULL,
   EventData       VARBINARY(MAX)  NOT NULL
)

Observed Gains

The impact on GitHub.com was substantial: CPU consumption for running orchestrations dropped by an average of 50%, with longer-running orchestrations benefiting the most. The orchestration platform had not received much investment before this change, and the results underscored the importance of continually reevaluating core infrastructure as the system scales.

Cutting Postback Noise

Understanding Postbacks

As a workflow run progresses, its status is reflected in checks visible through the UI and API. The GitHub Actions backend service manages the run state during orchestration, while the Rails monolith handles the checks. A “postback” is the service-to-service call that pushes the latest run state to checks. The backend maintains an internal queue of postbacks, each delivered via a separate orchestration per workflow run to ensure reliability across service downtime.

Visualization of a postback

Load testing revealed two problems. First, delivering a postback was consistently one of the slowest activities, averaging 250–300 ms per execution. Second, the backend was overly chatty: it sent one postback for every check step update, and three postbacks with nearly identical payloads whenever a check run completed. In large matrix scenarios, this volume of slow postbacks consumed significant system resources and could stall other activities.

Reducing and Streamlining Postbacks

The team audited the usefulness of each postback type. Check step statuses turned out to be displayed on only one specific UI screen, so the backend stopped sending step-level updates during the workflow run and now posts that data only upon completion. This introduced a tradeoff: in-progress runs may load slower initially due to client-side rendering overhead when step data isn’t yet available. The duplicated job-completed events were also removed. These changes shipped with GHES 3.3, enabling GitHub Actions to run close to twice as many concurrent jobs as GHES 3.2.

Addressing the per-postback latency is a separate effort. Postbacks were being sent over HTTP across four different services, with each service manually handling retries and timeouts. The team is now moving postback delivery to a message queue-based system that should be faster and simpler, with a rollout expected in the coming months.

Lessons for Scaling Platforms

These optimizations—and others not detailed here—collectively allow GitHub Actions to run three times more concurrent jobs while consuming fewer system resources. Beyond the immediate performance gains, the work reinforced a valuable engineering principle: it is always worth revisiting the fundamentals of long-standing systems as they scale. The team plans to improve load testing automation to catch issues like these earlier and continue optimizing performance across the platform.