GitHub Packages — Complete Reference Guide & Hands-On Tutorial

Last Verified: September 2026
Scope: GitHub.com / GitHub Enterprise Cloud unless explicitly stated otherwise
Audience: Developers, DevOps engineers, platform engineers, administrators, architects, trainers, and engineering teams

GitHub Packages is GitHub’s package-hosting platform. It lets teams publish, store, secure, discover, automate, and consume software packages and container images while keeping package ownership, source code, CI/CD, permissions, security metadata, and billing close to the GitHub development workflow.

This guide is organized as a progressive learning journey: concept → why → how → example → practice → real-world use case → best practices → troubleshooting.


1. What GitHub Packages Is

GitHub Packages is a set of package registries integrated with GitHub. A package is a distributable software artifact such as a container image, an npm library, a Java artifact, a NuGet package, or a Ruby gem.

Why teams use it

A typical software organization produces more than source code. It also produces reusable libraries, container images, command-line tools, SDKs, internal frameworks, application dependencies, and release artifacts. Those outputs need a trusted place to live.

GitHub Packages solves that problem by giving teams a registry that is integrated with:

  • GitHub repositories
  • GitHub organizations and teams
  • GitHub Actions
  • GitHub permissions and tokens
  • GitHub Releases
  • Supply-chain security features
  • Artifact attestations
  • Linked artifacts
  • REST APIs and webhooks
  • Billing and usage controls

Core lifecycle

flowchart LR
    A[Source Code] --> B[Build]
    B --> C[Test]
    C --> D[Package]
    D --> E[Authenticate]
    E --> F[Publish]
    F --> G[Registry]
    G --> H[Install / Pull]
    H --> I[Application or Deployment]
    I --> J[Observe / Audit]
    J --> K[Update / Retire]

The important idea is that a registry sits between producers and consumers. Producers create immutable versions; consumers fetch known versions through an authenticated and governed channel.

GitHub Packages vs GitHub Releases vs Actions artifacts

CapabilityGitHub PackagesGitHub ReleasesGitHub Actions artifacts
Primary purposeDependency/package distributionProduct release publishingTemporary workflow outputs
Typical consumersApplications, builds, deploymentsHumans and release automationWorkflow jobs and engineers
Versioned package semanticsYesRelease/tag semanticsNo package-manager semantics
Package manager integrationYesNoNo
Container registryYesNoNo
Long-term reusable dependencyYesSometimesUsually no
CI intermediate filePossible but not idealNoYes
Best exampleghcr.io/acme/api:1.4.2v1.4.2 release + binariestest reports, build outputs

A useful rule:

  • Use Packages when another build, runtime, or developer will consume the artifact as a package.
  • Use Releases when you are publishing a release event, release notes, and downloadable assets.
  • Use Actions artifacts for workflow-produced files that are primarily tied to a workflow run.

2. Supported Registries and Permission Models

GitHub Packages supports these principal registries:

EcosystemRegistry / endpointTypical clientPermission model
Containersghcr.ioDocker / OCI clientsGranular user/org scoped
npmnpm.pkg.github.comnpm / YarnGranular user/org scoped
NuGetnuget.pkg.github.comdotnet / NuGet CLIGranular user/org scoped
RubyGemsrubygems.pkg.github.comgem / BundlerGranular user/org scoped
Apache Mavenmaven.pkg.github.comMavenRepository scoped
GradleMaven-compatible GitHub Packages endpointGradleRepository scoped

GitHub’s legacy Docker registry used docker.pkg.github.com. It has been replaced by the Container registry at ghcr.io and should be treated primarily as a migration/backward-compatibility topic.

Granular packages vs repository-scoped packages

This is one of the most important architectural distinctions in GitHub Packages.

Granular-permission packages

Supported by:

  • Container registry
  • npm
  • NuGet
  • RubyGems

These packages can be scoped to a personal account or organization. Their visibility and access can be managed separately from a repository, although they can be connected to one.

Repository-scoped packages

Supported by:

  • Apache Maven
  • Gradle

These packages inherit their repository’s permissions and visibility.

Package scope model

flowchart TD
    A[GitHub Package]
    A --> B{Registry permission model}
    B -->|Granular| C[User or Organization Scope]
    C --> D[Optional repository connection]
    C --> E[Independent Read / Write / Admin]
    B -->|Repository scoped| F[Repository]
    F --> G[Repository visibility]
    F --> H[Repository permissions]

3. Core Concepts and Terminology

TermMeaning
PackageA distributable software artifact managed by a registry
RegistryService that stores and distributes package versions
Package managerClient that publishes or installs packages
NamespaceOwner/name space used to prevent naming ambiguity
Package ownerUser, organization, or repository that controls package scope
VersionA named package release such as 1.4.2
TagA movable or human-friendly alias, especially common for containers
DigestContent-addressed immutable identifier, especially for OCI images
MetadataDescription, source repository, license, tags, publication data, etc.
VisibilityPublic, private, or internal where supported
PermissionRead, write, or admin access
Repository connectionAssociation between a granular package and source repository
PublisherUser or automation that creates a package version
ConsumerBuild, developer, service, or deployment that downloads the package

Version vs tag vs digest

For containers, these three terms must not be confused:

  • Version: a package version as represented by GitHub’s package system.
  • Tag: a friendly image reference such as 1.8.0, main, or latest.
  • Digest: immutable content identifier such as sha256:....

For production deployment, digest pinning is the strongest way to guarantee that the exact bytes tested are the bytes deployed.


4. Where Packages Appear in GitHub

GitHub exposes packages from multiple contexts:

Organization packages

Navigate to the organization and select Packages. This is the natural management surface for shared internal libraries and container images owned by an organization.

User packages

A user’s profile can expose packages scoped to that user.

Repository packages

A repository sidebar can show packages associated with that repository. For repository-scoped registries, the repository is the package’s permission boundary. For granular registries, the repository is a connection that can provide source metadata and optionally inherited access.

Package search and discovery

Use organization, user, or repository Packages surfaces to browse packages you can access. From a package landing page, follow source-repository and version links to understand ownership and usage. For private/internal packages, discovery is permission-aware: a package that exists but is outside your access boundary may not be visible as a normal browsable asset.

Package landing page

A package page can expose:

  • Description
  • Installation and usage instructions
  • Source repository
  • Version history
  • Publication dates
  • Download activity
  • Metadata
  • Package settings
  • Access configuration

5. Access Control, Visibility, and Permissions

Package roles

RoleTypical capability
ReadView metadata and download/install
WriteRead plus upload/publish
AdminPublish, delete/manage package, change access, grant permissions

For organization-scoped granular packages, access can be granted to individuals or teams. Organization owners also have administrative control.

Visibility

Depending on the registry and account context, packages can be:

  • Public — intended for broad consumption.
  • Private — restricted to explicitly authorized users/repositories.
  • Internal — organization/enterprise-oriented visibility where supported by granular registries and plan context.

For most GitHub Packages registries, even public packages require package-client authentication. The major exception is the Container registry: public container images can be pulled anonymously.

Permission inheritance

When a granular package is connected to a repository, GitHub can let the package inherit repository permissions. Organizations can also disable automatic inheritance for newly published packages.

Use inheritance when repository membership should define package access. Use explicit granular access when one package is shared across many repositories or teams.

Repository transfer behavior

Repository transfers affect the two package models differently:

  • Granular packages: the package remains owned by its user/organization scope. A repository connection can be removed during transfer, and workflows/Codespaces can lose access.
  • Repository-scoped packages: the package follows the repository and its ownership/permission model.

This is why migration planning must include package ownership, not only repository ownership.


6. Authentication and Authorization

Authentication answers who are you? Authorization answers what may you do?

Personal access token (classic)

GitHub Packages package-client authentication uses a personal access token (classic) with package scopes.

ScopePurposeTypical minimum package permission
read:packagesDownload/installRead
write:packagesPublish/uploadWrite
delete:packagesDeleteAdmin

For organizations using SAML SSO, the token may also need authorization for that organization.

GITHUB_TOKEN

Inside GitHub Actions, prefer the repository’s automatically generated GITHUB_TOKEN where possible.

Example minimum workflow permissions for publishing:

permissions:
  contents: read
  packages: write

For a consumer workflow that only installs a package:

permissions:
  contents: read
  packages: read

For cross-repository use of granular packages, grant the consumer repository access under the package’s Manage Actions access settings.

Important distinction: package clients vs REST API tokens

Do not generalize token rules blindly.

  • Package-manager authentication is documented around PAT classic and GITHUB_TOKEN in Actions.
  • Some Packages REST API endpoints also support GitHub App user/installation access tokens or fine-grained token types as documented per endpoint.

Always follow the authentication section of the exact API endpoint you are using.

Secure token handling

Recommended:

  • Prefer GITHUB_TOKEN in Actions.
  • Use minimal permissions.
  • Store long-lived credentials in GitHub secrets or an external secret manager.
  • Rotate PATs.
  • Revoke unused credentials.
  • Avoid tokens in checked-in .npmrc, settings.xml, nuget.config, Gemfile, shell scripts, or Dockerfiles.
  • Treat self-hosted runners as part of your security boundary.
  • Pin third-party Actions to trusted commit SHAs for high-assurance release workflows.

Avoid:

  • Shared developer PATs
  • Admin-scoped tokens for read-only consumers
  • Printing tokens in diagnostic output
  • Publishing from untrusted pull-request contexts
  • Reusing the same token across unrelated systems

7. Package Lifecycle

A healthy package lifecycle is deliberate and auditable.

stateDiagram-v2
    [*] --> Build
    Build --> Test
    Test --> Publish: pass
    Test --> Build: fail/fix
    Publish --> Active
    Active --> NewVersion
    NewVersion --> Active
    Active --> Deprecated: ecosystem supports deprecation
    Active --> Deleted
    Deleted --> Restored: within restore rules
    Deleted --> [*]: retention window expires

Typical lifecycle stages

  1. Build source.
  2. Run unit/integration/security tests.
  3. Choose version.
  4. Authenticate.
  5. Publish package.
  6. Verify package metadata.
  7. Grant consumer access.
  8. Install/pull by consumers.
  9. Publish new versions rather than overwriting releases.
  10. Retire old versions based on retention policy.
  11. Restore only when supported and still inside the restoration window.

Semantic Versioning

For libraries, Semantic Versioning is a useful convention:

  • MAJOR: incompatible change
  • MINOR: backward-compatible feature
  • PATCH: backward-compatible fix

Example:

2.7.4
│ │ └─ patch
│ └─── minor
└───── major

Pre-releases can use identifiers such as:

2.8.0-alpha.1
2.8.0-beta.2
2.8.0-rc.1

Container image tags can mirror SemVer, but production deployment should prefer immutable digests.


8. Getting Started: Publish a Container to GHCR

Containers are the easiest way to see the complete GitHub Packages workflow end to end.

Prerequisites

  • GitHub account
  • Repository such as acme/hello-api
  • Docker installed locally, or GitHub Actions
  • Permission to publish to the target namespace

Example application

Create a minimal Dockerfile:

FROM nginx:alpine
COPY ./index.html /usr/share/nginx/html/index.html

LABEL org.opencontainers.image.source="https://github.com/acme/hello-api"
LABEL org.opencontainers.image.description="Hello API training image"
LABEL org.opencontainers.image.licenses="MIT"

Create index.html:

<h1>Hello from GitHub Container Registry</h1>

Authenticate locally

Export a PAT classic with the minimum required scopes:

export CR_PAT="YOUR_TOKEN"
echo "$CR_PAT" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin

Build and tag

docker build -t hello-api:1.0.0 .
docker tag hello-api:1.0.0 ghcr.io/acme/hello-api:1.0.0

Push

docker push ghcr.io/acme/hello-api:1.0.0

Verify

docker pull ghcr.io/acme/hello-api:1.0.0

For a public container, an anonymous pull can work without a registry login.

Pull by digest

After publishing, capture the image digest and deploy the immutable reference:

docker pull ghcr.io/acme/hello-api@sha256:REPLACE_WITH_DIGEST

Expected result

The package appears under the owning user/organization’s Packages page. If source metadata is configured correctly, the package page can link back to its repository.


9. GitHub Container Registry Deep Dive

What GHCR stores

GHCR supports Docker Image Manifest V2 Schema 2 and OCI image specifications. It can host Docker and OCI images, including foreign layers such as Windows image layers.

Common reference structure

ghcr.io/OWNER/IMAGE:TAG

Examples:

ghcr.io/acme/payments-api:2.3.1
ghcr.io/acme/payments-api:sha-4c912ab
ghcr.io/acme/payments-api@sha256:...

OCI metadata

Useful OCI annotations/labels include:

org.opencontainers.image.source
org.opencontainers.image.description
org.opencontainers.image.licenses
org.opencontainers.image.revision
org.opencontainers.image.version

These improve traceability and package presentation.

Tagging strategy

A strong release can publish several tags pointing to the same digest:

2.4.1
2.4
2
sha-a1b2c3d

Use latest only if your team defines exactly what it means. Never use latest as the sole production deployment reference.

Multi-platform images

With Buildx, one logical image tag can point to an OCI image index containing multiple platform manifests.

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t ghcr.io/acme/api:2.4.1 \
  --push .
flowchart TD
    A[ghcr.io/acme/api:2.4.1] --> B[OCI Image Index]
    B --> C[linux/amd64 manifest]
    B --> D[linux/arm64 manifest]

Container registry best practices

  • Use multi-stage builds.
  • Use small, maintained base images.
  • Add OCI source/license metadata.
  • Publish immutable release tags.
  • Add commit-SHA tags for traceability.
  • Capture and promote digests.
  • Generate provenance attestations.
  • Generate an SBOM.
  • Scan dependencies and base images.
  • Use least-privilege workflow permissions.

10. npm Registry

GitHub’s npm registry is used for Node.js packages. It supports granular user/organization-scoped permissions.

Package naming

GitHub Packages npm packages are scoped packages. A typical package name is:

{
  "name": "@acme/shared-utils",
  "version": "1.2.0",
  "description": "Shared utilities for Acme services",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/acme/shared-utils.git"
  }
}

Configure .npmrc

@acme:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}

Do not commit a literal token into .npmrc.

Publish locally

export NODE_AUTH_TOKEN="YOUR_PAT_CLASSIC"
npm publish

Install

npm install @acme/shared-utils@1.2.0

GitHub Actions publishing

name: Publish npm package

on:
  release:
    types: [published]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v7
        with:
          node-version: '20.x'
          registry-url: 'https://npm.pkg.github.com'
          scope: '@acme'

      - run: npm ci
      - run: npm test
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

npm use case

A company maintains a shared TypeScript library consumed by ten internal services. The package is owned by the organization, write access is limited to the platform team, and each consuming repository receives read access through Manage Actions access.

npm gotchas

  • Ensure the package is scoped to the expected user/organization.
  • Ensure .npmrc routes only the intended scope to GitHub Packages.
  • Avoid routing all npm traffic to a private registry unless that is explicitly intended.
  • Keep npmjs.org and GitHub Packages sources clear to avoid dependency confusion and accidental publication.

11. Apache Maven Registry

Maven packages on GitHub Packages are repository-scoped. This means the package follows repository permissions rather than having an independent granular permission model.

Maven coordinates

A Maven artifact is identified by:

groupId:artifactId:version

Example:

com.acme:payments-sdk:2.1.0

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.acme</groupId>
  <artifactId>payments-sdk</artifactId>
  <version>2.1.0</version>

  <distributionManagement>
    <repository>
      <id>github</id>
      <name>GitHub Packages</name>
      <url>https://maven.pkg.github.com/acme/payments-sdk</url>
    </repository>
  </distributionManagement>
</project>

settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
  <servers>
    <server>
      <id>github</id>
      <username>${env.GITHUB_ACTOR}</username>
      <password>${env.GITHUB_TOKEN}</password>
    </server>
  </servers>
</settings>

Publish

mvn --batch-mode deploy

Consume

A consumer repository can configure the GitHub Packages Maven repository and add a dependency:

<dependency>
  <groupId>com.acme</groupId>
  <artifactId>payments-sdk</artifactId>
  <version>2.1.0</version>
</dependency>

GitHub Actions

name: Publish Maven package

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - run: mvn --batch-mode verify
      - run: mvn --batch-mode deploy
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Maven design implication

Because Maven packages are repository-scoped, do not design access as though the package has independent organization-level ACLs. If many teams need the package, model the repository permissions accordingly.


12. Gradle Registry

GitHub’s Gradle support uses Maven-format package publishing and is repository-scoped.

Groovy DSL example

plugins {
    id 'java-library'
    id 'maven-publish'
}

version = '1.3.0'
group = 'com.acme'

publishing {
    publications {
        mavenJava(MavenPublication) {
            from components.java
        }
    }

    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/acme/shared-java")
            credentials {
                username = System.getenv("GITHUB_ACTOR")
                password = System.getenv("GITHUB_TOKEN")
            }
        }
    }
}

Kotlin DSL example

plugins {
    `java-library`
    `maven-publish`
}

group = "com.acme"
version = "1.3.0"

publishing {
    publications {
        create<MavenPublication>("mavenJava") {
            from(components["java"])
        }
    }

    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/acme/shared-java")
            credentials {
                username = System.getenv("GITHUB_ACTOR")
                password = System.getenv("GITHUB_TOKEN")
            }
        }
    }
}

Publish

./gradlew publish

GitHub Actions

name: Publish Gradle package

on:
  release:
    types: [created]

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4

      - run: ./gradlew test
      - run: ./gradlew publish
        env:
          GITHUB_ACTOR: ${{ github.actor }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

For high-assurance release pipelines, pin third-party actions to a reviewed commit SHA rather than a moving tag.


13. NuGet Registry

GitHub Packages supports NuGet packages with granular user/organization-scoped permissions.

Add a package source

dotnet nuget add source \
  --username YOUR_GITHUB_USERNAME \
  --password YOUR_PAT_CLASSIC \
  --store-password-in-clear-text \
  --name github \
  "https://nuget.pkg.github.com/acme/index.json"

The --store-password-in-clear-text option is convenient for demonstrations but is a security tradeoff. In CI, prefer injected credentials and ephemeral runners.

Pack

dotnet pack --configuration Release

Publish

dotnet nuget push "bin/Release/Acme.Tools.1.0.0.nupkg" \
  --api-key YOUR_PAT_CLASSIC \
  --source github

Install

dotnet add package Acme.Tools --version 1.0.0 --source github

nuget.config with source mapping

Source mapping helps prevent dependency confusion by ensuring private package IDs resolve only from the intended source.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="github" value="https://nuget.pkg.github.com/acme/index.json" />
  </packageSources>

  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
    <packageSource key="github">
      <package pattern="Acme.*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

Current size note

GitHub documents that a NuGet .nupkg archive for a package version must be smaller than 2.147 GB.


14. RubyGems Registry

GitHub Packages supports Ruby gems with granular user/organization-scoped permissions.

Prerequisites

GitHub’s current documentation lists RubyGems 2.4.1+ and Bundler 1.6.4+ as minimums. In production, use a supported modern Ruby/RubyGems/Bundler toolchain.

Authenticate with RubyGems

gem sources --add \
  https://YOUR_USERNAME:YOUR_PAT_CLASSIC@rubygems.pkg.github.com/acme/

Authenticate with Bundler

bundle config https://rubygems.pkg.github.com/acme \
  YOUR_USERNAME:YOUR_PAT_CLASSIC

Gem metadata

Gem::Specification.new do |spec|
  spec.name        = "acme-tools"
  spec.version     = "1.0.0"
  spec.summary     = "Internal Acme Ruby utilities"
  spec.files       = Dir["lib/**/*"]
  spec.require_paths = ["lib"]
  spec.metadata = {
    "github_repo" => "ssh://github.com/acme/acme-tools"
  }
end

Build and publish

gem build acme-tools.gemspec
gem push --key github \
  --host https://rubygems.pkg.github.com/acme \
  acme-tools-1.0.0.gem

Bundler consumption

source "https://rubygems.org"

source "https://rubygems.pkg.github.com/acme" do
  gem "acme-tools", "1.0.0"
end

15. Connecting Packages to Repositories

For granular registries, package ownership and repository ownership are separate concepts.

Why connect a package to a repository?

A connection can provide:

  • Source repository link
  • README/context on the package page
  • Permission inheritance, if enabled
  • Automatic Actions access from the linked repository
  • Better provenance and discoverability

Manual connection

Typical UI flow:

  1. Open the package landing page.
  2. Open Package settings.
  3. Find the repository connection area.
  4. Choose Connect repository.
  5. Select the source repository.
  6. Decide whether repository permissions should be inherited.
  7. Verify Actions/Codespaces access if needed.

Automatic container linkage

OCI source metadata can establish repository context:

LABEL org.opencontainers.image.source="https://github.com/acme/payments-api"

Design pattern: one producer, many consumers

flowchart LR
    P[Producer repository] --> W[Build/Test workflow]
    W --> R[Organization package]
    R --> C1[Consumer repo A]
    R --> C2[Consumer repo B]
    R --> C3[Consumer repo C]

For this pattern, granular permissions are ideal: the package can remain organization-owned while individual repositories receive only read access.


16. GitHub Actions and GitHub Packages

GitHub Actions is the preferred automation layer for build-test-publish workflows.

Standard pipeline

flowchart LR
    A[Push / Tag / Release] --> B[Checkout]
    B --> C[Build]
    C --> D[Test]
    D --> E[Package]
    E --> F[Authenticate]
    F --> G[Publish]
    G --> H[Verify]
    H --> I[Attest / SBOM]

Trigger choices

TriggerGood forRisk / consideration
push to branchSnapshot/dev buildsCan create many versions
Git tagVersion-driven releasesProtect tag creation
GitHub ReleaseHuman-reviewed release flowRelease creation must be governed
workflow_dispatchManual controlled publishRequires operational discipline
schedulePeriodic rebuildsMust avoid accidental republish collision

Recommended release workflow permissions

permissions:
  contents: read
  packages: write
  attestations: write
  id-token: write

Only request attestations and id-token when the workflow actually generates attestations.

Test before publish

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - run: ./scripts/test.sh

  publish:
    needs: test
    if: github.event_name == 'release'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v6
      - run: ./scripts/publish.sh

The important control is the dependency: publishing does not start unless tests succeed.


17. Container Publishing Workflow with Metadata and Provenance

A production-quality container workflow should build, test, publish, capture the digest, and generate provenance.

name: Publish container

on:
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      attestations: write
      id-token: write

    steps:
      - uses: actions/checkout@v6

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate image metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha

      - name: Build and push
        id: push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Generate provenance attestation
        uses: actions/attest@v4
        with:
          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          subject-digest: ${{ steps.push.outputs.digest }}
          push-to-registry: true

For high-assurance production workflows, pin non-GitHub and third-party Actions to a reviewed full commit SHA. The major-version tags above are easier for training and should not be mistaken for the strongest supply-chain posture.

Multi-platform extension

Add Buildx setup and configure:

with:
  platforms: linux/amd64,linux/arm64
  push: true

18. Actions and Codespaces Package Access

Granular packages can grant access to repositories separately for GitHub Actions and GitHub Codespaces.

Actions access

Use package settings when a workflow in one repository needs a package owned elsewhere.

Typical flow:

  1. Open the package.
  2. Open Package settings.
  3. Find Manage Actions access.
  4. Add the workflow repository.
  5. Choose the minimum required role.
  6. Set workflow permissions to packages: read or packages: write as appropriate.
  7. Test with GITHUB_TOKEN before introducing a PAT.

Codespaces access

For supported granular packages, package settings can also allow selected repositories’ Codespaces to install the package. This is useful when developer environments need private dependencies but you do not want developers maintaining broad package credentials.

Cross-repository access decision

flowchart TD
    A[Workflow needs package] --> B{Same repository/package relationship?}
    B -->|Yes| C[Use GITHUB_TOKEN]
    B -->|No| D{Granular registry?}
    D -->|Yes| E[Grant repository package access]
    E --> C
    D -->|No / repo-scoped| F[Use repository permission model]
    F --> G[Validate token and repository access]

19. Deleting, Restoring, and Cleaning Up Packages

Deletion is a destructive operation and should be governed like a release operation.

Current GitHub.com rules

GitHub documents that users with sufficient access can delete:

  • An entire private package.
  • A specific version of a private package.
  • A public package if no version has more than 5,000 downloads.
  • A public package version if that version does not have more than 5,000 downloads.

For a public package/version above the documented 5,000-download threshold, GitHub Support is required for deletion assistance.

Restoration

A deleted package or package version can normally be restored when:

  • It is restored within 30 days of deletion.
  • The package namespace/version has not been reused in a way that blocks restoration.

GitHub Actions deletion/restoration

GitHub currently documents workflow-based delete/restore through the REST API as public preview. For granular packages, the workflow repository must have package admin access and the workflow token must be configured appropriately.

Safe cleanup policy

A practical organization policy could be:

Package classRetention suggestion
Production releasesKeep indefinitely or per compliance policy
Supported major/minor releasesKeep while supported
Release candidatesKeep last 5–10
Branch/dev buildsKeep 7–30 days
Pull-request buildsKeep only while PR is active plus grace period
Untagged container manifestsClean after verifying no digest references depend on them

Example: list container versions with GitHub CLI

gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages/container/payments-api/versions?per_page=100"

Example: delete a version

gh api \
  --method DELETE \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages/container/payments-api/versions/PACKAGE_VERSION_ID"

Use a dry-run/reporting step before automated deletion.


20. REST API for GitHub Packages

The Packages REST API can list and manage packages and versions for users and organizations.

Common package types

The REST API recognizes package types including:

npm
maven
rubygems
docker
nuget
container

Gradle packages use the API package type maven. Legacy Docker-registry images can be represented by docker, while GHCR images use container.

List organization packages

gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages?package_type=container&per_page=100"

Get a package

gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages/container/payments-api"

List versions

gh api \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages/container/payments-api/versions?state=active&per_page=100"

Pagination

The API defaults to paginated responses. For large estates:

gh api --paginate \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages?package_type=container&per_page=100"

API authentication caution

Endpoint support differs by operation. PAT classic is the baseline package token model, while current REST documentation also lists GitHub App and fine-grained token support for selected endpoints. Do not assume every endpoint accepts every token type.


21. GitHub CLI for Package Administration

There is no need to wait for a dedicated gh package command to automate package operations. gh api can call the Packages REST API directly.

Inventory script

#!/usr/bin/env bash
set -euo pipefail

ORG="${1:?usage: $0 ORG}"
TYPE="${2:-container}"

gh api --paginate \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/${ORG}/packages?package_type=${TYPE}&per_page=100" \
  --jq '.[] | [.name, .visibility, .version_count, .updated_at] | @tsv'

Example output:

payments-api    private    42    2026-09-24T03:10:21Z
shared-ui       internal   18    2026-09-20T09:04:03Z

Why gh api is useful

  • Auth can reuse GitHub CLI login.
  • Pagination is easy.
  • --jq supports lightweight reporting.
  • Scripts can run locally or in Actions.
  • It exposes new REST features without waiting for a specialized CLI subcommand.

22. GraphQL and GitHub Packages

Treat GraphQL support as a compatibility area, not the default management API for GitHub Packages.

GitHub documents that the Packages GraphQL API cannot be used with registries that support granular permissions. GraphQL remains relevant mainly to repository-scoped package models and historical integrations.

For new automation across Container, npm, NuGet, and RubyGems, prefer the REST API unless a documented GraphQL object is specifically required.

Decision table

NeedPreferred interface
List/manage modern granular packagesREST API
Automate from shellgh api
Receive publish eventWebhook
Package manager publish/installNative package client
Historical/repository-scoped graph use caseGraphQL where documented

23. Package Webhooks and Event Automation

GitHub exposes a package webhook event for GitHub Packages activity. The documented published action fires when a package is published.

Typical automation

sequenceDiagram
    participant Publisher
    participant GitHubPackages as GitHub Packages
    participant Webhook
    participant Scanner
    participant Chat as Notification System

    Publisher->>GitHubPackages: Publish package
    GitHubPackages->>Webhook: package.published
    Webhook->>Scanner: Start external scan
    Scanner-->>Webhook: Result
    Webhook->>Chat: Publish status

Example use cases

  • Start an external vulnerability scan after publication.
  • Synchronize metadata to a CMDB.
  • Notify a release channel.
  • Trigger downstream deployment orchestration.
  • Register the artifact in an internal catalog.

package vs registry_package

GitHub’s webhook documentation recommends the newer package event over the older registry_package event. Existing integrations should be reviewed before migration rather than changed blindly.

Webhook security

  • Use a webhook secret.
  • Validate GitHub signatures.
  • Reject stale/replayed requests where practical.
  • Make handlers idempotent.
  • Do not treat an incoming publish event as sufficient authorization for production deployment.

24. GitHub App Integration

GitHub Apps are appropriate when a service needs organization-wide automation without relying on a person’s credentials.

Possible responsibilities:

  • Receive package webhooks.
  • Read package metadata through supported REST endpoints.
  • Create audit records.
  • Enforce internal policy outside GitHub.
  • Integrate package events with deployment or security tooling.

App design principles

  • Request only required repository/organization permissions.
  • Use short-lived installation access tokens.
  • Verify webhook signatures.
  • Store App private keys securely.
  • Model each REST endpoint’s accepted token types explicitly.

25. Billing, Storage, Data Transfer, and Budgets

GitHub Packages usage is tied to the owning account and plan.

Included usage currently documented by GitHub

PlanIncluded storageIncluded data transfer / month
GitHub Free500 MB1 GB
GitHub Pro2 GB10 GB
GitHub Free for organizations500 MB1 GB
GitHub Team2 GB10 GB
GitHub Enterprise Cloud50 GB100 GB

GitHub documents that the included storage pool is shared with GitHub Actions artifacts.

Container registry billing status

As of September 2026, GitHub’s billing documentation says Container registry image storage and bandwidth are currently free, with at least one month of notice promised before a policy change.

Treat that as a current policy, not a permanent architecture assumption.

Actions package transfer

GitHub documents that package downloads performed by GitHub Actions using a GITHUB_TOKEN do not count against the hosting repository’s data-transfer usage. Current billing documentation further distinguishes token and runner type: GITHUB_TOKEN access is free for GitHub-hosted and self-hosted runners; PAT-based package access is documented as free on GitHub-hosted runners but billable on self-hosted runners.

Cost controls

Recommended:

  • Define version-retention rules.
  • Clean snapshot/dev packages.
  • Avoid duplicating the same large binary under unnecessary versions.
  • Monitor package and Actions storage together.
  • Configure budgets/spending limits where applicable.
  • Enable budget alerts at 75%, 90%, and 100% where useful.
  • Enable included-usage alerts at 90% and 100% for GitHub Packages storage/bandwidth.
  • Review sudden changes in download patterns.
  • Track organization ownership before moving repositories/packages.

Storage forecasting

A simple estimate:

Monthly retained storage growth
≈ average package size × new retained versions per month × package count

For containers, deduplicated layer behavior can make real storage different from a naive image-size multiplication, so billing/usage reports remain the source of truth.


26. Organization Package Administration

Organization owners should treat package configuration as part of platform governance.

Organization-level concerns

  • Who may create packages?
  • Which visibility levels are allowed?
  • Should granular packages inherit repository permissions automatically?
  • Which teams can administer shared packages?
  • Who may publish production packages?
  • What is the naming convention?
  • What is the retention policy?
  • How are external/public packages reviewed?
  • What is the response plan for a compromised package?

Suggested organization standard

AreaRecommended standard
OwnershipOrganization, not individual users, for business packages
PublishingCI-first; developer-laptop publishing only for controlled exceptions
CredentialsGITHUB_TOKEN in Actions; minimal PAT classic for local use
VisibilityPrivate/internal by default for proprietary packages
Source linkRequired
VersioningSemVer for libraries; immutable release tags/digests for containers
ReleaseProtected tags/releases or environment approval
RetentionExplicit per package class
ProvenanceAttest production artifacts
SBOMGenerate for production/security-sensitive artifacts

27. Enterprise Considerations

Enterprise Managed Users

GitHub documents that Enterprise Managed Users can publish to organization namespaces but cannot publish packages to their own user namespace because there is no personal storage allocation.

GitHub Enterprise Cloud

Enterprise Cloud is relevant when you need organization/enterprise governance, internal visibility, broader security capabilities, or private/internal artifact attestations.

For GitHub Enterprise Cloud with data residency, do not assume the GitHub.com registry matrix is identical. Current GitHub documentation says the Maven and Gradle registries are unavailable in the data-residency offering. Service URLs can also differ; for example, the Container registry uses a tenant-specific containers.SUBDOMAIN.ghe.com endpoint rather than ghcr.io. Review the exact GHE.com feature matrix before migration.

Artifact attestations and plan behavior

GitHub currently documents artifact attestations as available on current plans, with an important visibility distinction:

  • On Free, Pro, and Team, attestations are available for public repositories.
  • Private/internal repository attestations require GitHub Enterprise Cloud.

Always recheck plan requirements before making compliance commitments.

GitHub Enterprise Server

GitHub Enterprise Server capabilities vary by server version. Do not copy GitHub.com registry availability, action versions, API versions, or security-feature assumptions directly to GHES. Check the documentation for the exact GHES release you operate.


28. Package Architecture Patterns

Centralized organization package registry

flowchart TB
    subgraph Producers
      A[Service A Repo]
      B[Library Repo]
      C[UI Repo]
    end

    A --> P[Organization Packages]
    B --> P
    C --> P

    P --> X[Consumer Repo 1]
    P --> Y[Consumer Repo 2]
    P --> Z[Runtime / Deployment]

Benefits:

  • Central ownership
  • Easier discovery
  • Consistent access model
  • Shared CI patterns
  • Easier audit and retention

Multi-repository shared library

Producer repository publishes @acme/auth-client. Many services consume a pinned version. The producer team owns write/admin; consumers receive read-only access.

Monorepo with multiple packages

repo/
├── packages/
│   ├── api-client/
│   ├── ui-kit/
│   └── config/
├── .github/workflows/
└── package.json

Publishing options:

  • Independent package versions
  • One shared release version
  • Changed-package detection
  • Matrix publishing
  • Path filters

Use tools such as workspace managers/change detection only when their additional complexity is justified.


29. Build Once, Deploy Many

A mature release pipeline should avoid rebuilding the artifact independently for each environment.

flowchart LR
    A[Commit] --> B[Build + Test]
    B --> C[Immutable Artifact]
    C --> D[Dev]
    D --> E[Stage]
    E --> F[Production]

The artifact moving through environments should be the same bytes.

Containers

Good:

ghcr.io/acme/api@sha256:abc123...

Riskier:

ghcr.io/acme/api:latest

Promotion patterns

  • Promote a digest, not a rebuild.
  • Add an environment/release tag to an existing digest if your workflow needs tags.
  • Store release metadata that records the exact digest/version.
  • Roll back by redeploying a previously approved immutable version.

Why this matters

Rebuilding per environment creates a provenance gap: production may contain different bytes from the artifact tested in staging even when source revision is the same.


30. Versioning and Release Strategy

Libraries

Recommended:

  • Semantic versions
  • Clear compatibility policy
  • Pre-release identifiers
  • Changelog/release notes
  • No version reuse

Containers

Recommended tag set:

2.7.3
2.7
2
sha-74cce21

Optional:

latest
main
release-candidate

Only use mutable tags if your automation understands that they are pointers, not identities.

Release-driven publishing

A controlled release path:

flowchart LR
    A[Merge to main] --> B[CI]
    B --> C[Create version tag]
    C --> D[GitHub Release]
    D --> E[Publish package]
    E --> F[Attest]
    F --> G[Promote exact version]

Rollback

Do not overwrite 2.7.3 with fixed bytes. Publish 2.7.4 or redeploy a prior known-good immutable artifact.


31. Software Supply-Chain Security

Publishing a package is only one part of the problem. A secure software supply chain must answer:

  • Where did this artifact come from?
  • Which source revision produced it?
  • Which workflow built it?
  • Was the build trusted?
  • Has the artifact changed?
  • What dependencies are inside it?
  • Is it deployed to production?

Security model

flowchart LR
    A[Source] --> B[Trusted Workflow]
    B --> C[Build]
    C --> D[Package]
    D --> E[Provenance]
    D --> F[SBOM]
    D --> G[Registry]
    G --> H[Verification]
    H --> I[Deployment]
    I --> J[Runtime / Linked Artifact Context]

Core strategies

  • Pin dependencies where practical.
  • Use lockfiles.
  • Protect release branches/tags/workflows.
  • Minimize workflow token permissions.
  • Pin third-party Actions for high-assurance builds.
  • Use immutable package versions.
  • Use container digests for deployment.
  • Generate provenance attestations.
  • Generate SBOMs.
  • Scan dependencies and images.
  • Record runtime/deployment context.

32. Artifact Attestations and Build Provenance

Artifact attestations establish verifiable information about where and how an artifact was built.

Why provenance matters

Without provenance, a package digest proves identity but not origin. Provenance connects an artifact to trusted build metadata such as repository, workflow, and commit.

Workflow permissions

A provenance workflow commonly needs:

permissions:
  contents: read
  id-token: write
  attestations: write

For linked-artifact storage records using registry publishing, GitHub’s current linked-artifacts flow can also require:

artifact-metadata: write

Verify an attestation

For a built artifact:

gh attestation verify ./dist/my-binary \
  -R acme/my-project

For a container, verification uses the subject image/digest and the repository/owner context supported by gh attestation verify.

SLSA

SLSA is a framework for reasoning about supply-chain security and provenance. GitHub attestations can provide signed build provenance that contributes to a stronger SLSA-oriented build process. Do not claim a particular SLSA level solely because an attestation exists; the complete build system and controls matter.


33. Software Bill of Materials (SBOM)

An SBOM is a machine-readable inventory of software components and dependencies.

Common formats include:

  • SPDX
  • CycloneDX

Why SBOMs matter

They help teams answer:

  • Which components are inside this build?
  • Do we use a vulnerable library?
  • Which version is deployed?
  • What should be reviewed during an incident?

Attesting an SBOM

After generating an SBOM file, GitHub Actions can attach an SBOM attestation. GitHub’s current documentation supports SPDX and CycloneDX predicate flows.

Example verification for an SPDX SBOM attestation:

gh attestation verify PATH/TO/ARTIFACT \
  -R acme/repository \
  --predicate-type https://spdx.dev/Document/v2.3

SBOM is not a vulnerability scan

An SBOM tells you what is present. Vulnerability-management tooling tells you whether known vulnerabilities affect those components. Use both.


34. Dependency Graph and Dependabot

The dependency graph summarizes dependencies detected from repository manifests/lockfiles and dependencies submitted by supported automation/API flows.

It can show:

  • Direct dependencies
  • Transitive dependencies where supported
  • Versions
  • Licenses
  • Known vulnerabilities
  • Dependents for public ecosystems where available

Dependency data sources

GitHub can populate the graph through:

  • Static manifest/lockfile analysis
  • Dependabot graph jobs where supported
  • Automatic dependency submission
  • Dependency submission API

Why lockfiles matter

A lockfile records the exact resolved dependency set. That improves reproducibility and gives security tooling better evidence than a broad version range alone.

Dependabot integration

Once dependency data is present, GitHub can use it for:

  • Dependabot alerts
  • Dependabot security updates
  • Dependabot version updates
  • Dependency review in pull requests

Private registry access

If Dependabot must resolve private dependencies, configure private registry access according to the ecosystem and repository security settings. Do not place credentials in the dependency manifest itself.


35. Package Vulnerability Management

A package-security response should connect advisories, releases, and consumers.

Example response flow

flowchart TD
    A[Vulnerability identified] --> B[Assess affected versions]
    B --> C[Fix source/dependency]
    C --> D[Build + test]
    D --> E[Publish patched version]
    E --> F[Create advisory/release notes]
    F --> G[Update consumers]
    G --> H[Verify production rollout]

Practical controls

  • Track CVEs and GitHub Security Advisories (GHSA).
  • Publish patched versions rather than replacing existing versions.
  • Use Dependabot/security campaigns where appropriate.
  • Identify production impact using deployment/runtime context.
  • Revoke compromised credentials immediately.
  • Quarantine or delete malicious/accidentally published packages where safe and permitted.
  • Preserve evidence during supply-chain incidents.

36. Linked Artifacts

Linked artifacts provide an organization-level view of software artifacts built with GitHub Actions, even when the artifact is stored outside GitHub Packages.

The linked artifacts page is metadata-centric: it does not act as the registry storing the artifact bytes.

What it connects

flowchart LR
    A[GitHub Repository] --> B[GitHub Actions Build]
    B --> C[Artifact]
    C --> D[Storage Record]
    C --> E[Deployment Record]
    D --> F[Linked Artifacts]
    E --> F
    F --> G[Security / Audit / Policy Context]

Storage records

Storage records can describe:

  • Source repository
  • Artifact registry
  • Artifact repository, where applicable
  • Provenance/attestations
  • Build information

Deployment records

Deployment records can include:

  • Environment
  • Cluster/runtime context
  • Production status
  • Runtime risks such as public internet exposure
  • Sensitive-data context

GitHub explicitly distinguishes these records from the repository deployments dashboard; they are separate data sources.

Ways to populate linked artifacts

Current GitHub documentation lists:

  • Artifact-attestation workflows
  • JFrog Artifactory integration
  • Dynatrace integration
  • Microsoft Defender for Cloud integration
  • Custom integrations using the artifact metadata REST API

GitHub currently marks the Microsoft Defender for Cloud integration as public preview.

Attestation-based storage records

GitHub documents automatic storage-record creation when the attestation action uses:

push-to-registry: true

and the workflow has:

permissions:
  artifact-metadata: write

Why linked artifacts are useful

  • Trace a built artifact to source.
  • See where it is stored.
  • See whether it is deployed.
  • Prioritize Dependabot/code-scanning findings affecting production.
  • Export evidence for audit/compliance.
  • Associate runtime risk with source repositories.

37. Access Governance

A registry can become a shadow production system if package governance is weak.

Recommended access model

PersonaReadWriteAdmin
Application developerYesUsually noNo
Package maintainerYesYesSometimes
CI release workflowYesYesOnly when operationally required
Platform teamYesControlledYes for shared packages
Consumer serviceYesNoNo
Security/audit automationMetadata readNoNo

Team-based permissions

Prefer team access over individual grants for organization packages. This reduces long-term permission drift.

Repository inheritance

Use inheritance when the package’s lifecycle matches one repository. Break inheritance when the package is a shared organization resource requiring a different audience.


38. Visibility Governance

Default posture

For proprietary packages:

  1. Start private/internal.
  2. Grant read access intentionally.
  3. Make a package public only after review.

Public package checklist

Before changing visibility to public:

  • Confirm package contains no proprietary code.
  • Confirm no embedded credentials/secrets.
  • Confirm source/license metadata is correct.
  • Confirm public distribution is legally approved.
  • Confirm package name/namespace is correct.
  • Confirm published versions cannot disclose internal build paths or sensitive metadata.

Public package deletion has additional constraints, so publication should be treated as potentially irreversible at scale.


39. Credential Governance

Preferred order

For GitHub Actions:

  1. GITHUB_TOKEN
  2. GitHub App token when a service/app model is needed and endpoint support exists
  3. PAT classic only when required by package-client or cross-boundary behavior

For developer package-client use, PAT classic remains the normal GitHub Packages model.

Rotation policy

Define:

  • Owner
  • Purpose
  • Required scopes
  • Expiry
  • Rotation period
  • Revocation procedure
  • Incident response

Machine users

Do not create a machine user merely to avoid understanding package permissions. Prefer GitHub Apps or repository/organization automation patterns when they better match the use case.


40. Publication Governance

CI-only publishing

The strongest default is to publish production packages only from controlled CI.

Benefits:

  • Reproducibility
  • Centralized credentials
  • Audit trail
  • Consistent tests
  • Provenance generation
  • Easier policy enforcement

Protected release workflow

A common design:

flowchart TD
    A[PR reviewed] --> B[Merge]
    B --> C[CI passes]
    C --> D[Create protected tag/release]
    D --> E[Approval if required]
    E --> F[Publish]
    F --> G[Attest + SBOM]
    G --> H[Promote immutable artifact]

Naming convention example

Libraries:
  @acme/<domain>-<purpose>

Containers:
  ghcr.io/acme/<service>

Java:
  com.acme.<domain>:<artifact>

NuGet:
  Acme.<Domain>.<Package>

41. Consumption Governance

Consumption is as important as publication.

Recommended controls

  • Approved registries
  • Approved package sources
  • Lockfiles
  • Version constraints
  • Digest pinning for production containers
  • Vulnerability review
  • License review
  • Source mapping where supported
  • Explicit private registry authentication

Dependency confusion

A private package name can collide with a package in a public registry if source resolution is not controlled. Mitigations include scoped npm packages, NuGet package source mapping, explicit Maven/Gradle repository ordering, and private namespace standards.


42. CI/CD Design Patterns

Build → Test → Publish

The baseline pattern:

build → unit test → integration test → package → publish → verify

Release-driven publishing

Only release/tag events produce production packages.

Multi-job pipeline

flowchart LR
    A[Build] --> B[Unit Tests]
    B --> C[Integration Tests]
    C --> D[Security Checks]
    D --> E[Publish]
    E --> F[Attest]

Environment-gated publish

GitHub Environments can add approvals or protection controls before a production publishing job executes.

Reusable workflow

Centralize release logic in a reusable workflow so many repositories inherit the same permission model, tagging rules, and attestation behavior.


43. Monorepo and Multi-Package Publishing

Challenges

  • Which package changed?
  • Which version should increment?
  • Should all packages share one version?
  • How do you avoid publishing unaffected packages?

Changed-path matrix pattern

strategy:
  matrix:
    package:
      - api-client
      - ui-kit
      - config

Combine a matrix with changed-path detection or your monorepo tool of choice.

Independent versions

Best when packages have separate consumers and release cadences.

Shared versions

Best when packages form one tightly coupled product release.

Avoid publishing every package on every commit just because the repository contains many packages.


44. Container CI/CD Advanced Patterns

Multi-stage build

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html

Advantages:

  • Smaller runtime image
  • Build tools absent from final image
  • Reduced attack surface

Layer caching

BuildKit/Buildx can reuse unchanged layers. Cache configuration should never cause secrets or credentials to be baked into image layers.

Digest capture

The build/push action exposes the published digest. Save it as release metadata and use it for deployment/attestation.

Multi-architecture

Use Buildx and QEMU/native builders to publish AMD64 and ARM64 manifests under one tag.

Secure publishing

  • Minimal packages: write
  • Trusted trigger
  • Protected source
  • Pinned Actions
  • Provenance
  • SBOM
  • Vulnerability scanning
  • Digest-based deployment

45. Deployment Integration

GitHub Packages containers can be consumed by Docker and container orchestration platforms such as Kubernetes and cloud container services.

Kubernetes private image pull

A typical secret-based approach:

kubectl create secret docker-registry ghcr-pull \
  --docker-server=ghcr.io \
  --docker-username=YOUR_USERNAME \
  --docker-password=YOUR_PAT_CLASSIC \
  --namespace=my-app

Pod snippet:

apiVersion: v1
kind: Pod
metadata:
  name: api
spec:
  imagePullSecrets:
    - name: ghcr-pull
  containers:
    - name: api
      image: ghcr.io/acme/api@sha256:REPLACE_WITH_DIGEST

For production, evaluate workload identity, secret rotation, external secret managers, and platform-native registry integration rather than relying on manually maintained long-lived pull secrets.

Common deployment targets

TargetTypical GitHub Packages useMain consideration
Docker hostsPull GHCR image directlyCredential rotation and digest pinning
KubernetesimagePullSecrets or platform identityNamespace secret scope and rotation
Amazon ECS / EKSDeploy GHCR-hosted OCI imagePrivate-registry credentials / secret integration
Azure AKS / Container AppsDeploy OCI imageRegistry credentials and platform secret model
Google GKE / Cloud RunDeploy OCI imageExternal-registry authentication and egress policy
Other OCI runtimesPull by tag or digestVerify OCI compatibility and auth model

GitHub Packages is the artifact source; each runtime still has its own secure secret, identity, network, and deployment controls.


46. Package Automation Patterns

Automated publishing

Triggers can include:

  • Tag
  • GitHub Release
  • Protected branch
  • Manual dispatch
  • Schedule for rebuilds

Automated cleanup

A safe cleanup automation should:

  1. List versions.
  2. Classify protected releases.
  3. Identify candidates by age/count/tag.
  4. Print a dry-run report.
  5. Require approval for destructive classes if risk is high.
  6. Delete by immutable version ID.
  7. Log results.

Automated promotion

For containers, promotion should retag or record an existing digest rather than rebuild.

Event automation

Use package webhooks to trigger notifications, scanning, catalog registration, or downstream orchestration.


47. Troubleshooting Authentication

Authentication failures are usually caused by the wrong token type, insufficient scopes, SSO authorization, an incorrect registry endpoint, or a credential not being presented by the client.

SymptomLikely causeDiagnoseFix
401 UnauthorizedInvalid/expired tokenRe-authenticate; inspect token age/scopesReplace/rotate token
Login succeeds but install failsMissing read:packages or package ACLCheck package access and token scopeAdd minimum read access
Publish deniedMissing write:packages or write roleCheck token + package/repository permissionGrant write only where required
Works locally, fails in ActionsGITHUB_TOKEN permissions too narrowInspect workflow permissionsAdd packages: read/write
Cross-repo package not foundWorkflow repo not granted package accessPackage settings → Actions accessGrant repo access
Organization access failsPAT not SSO-authorizedCheck org SSO authorizationAuthorize token for org
Wrong endpointClient points to public/default registryInspect client configUse correct GitHub Packages endpoint

Diagnostic principle

Separate the layers:

  1. Can the client reach the registry?
  2. Is the credential accepted?
  3. Does the credential have the required scope?
  4. Does the underlying user/repository have package permission?
  5. Is the package in the expected namespace?

48. Troubleshooting Authorization

A valid token can still be unauthorized.

Read denied

Check:

  • Package visibility
  • User/team read role
  • Repository inheritance
  • Actions access grant
  • packages: read workflow permission
  • Repository-scoped package permissions

Write denied

Check:

  • Package write role
  • write:packages PAT scope
  • packages: write workflow permission
  • Publishing namespace
  • Repository ownership for Maven/Gradle

Admin denied

Deletion/access changes require admin-level control. A CI workflow should not receive admin unless its specific operation needs it.

Inheritance confusion

If a package inherits repository permissions, changing explicit package ACLs may not behave as expected until inherited access is removed. Understand which model is active before changing permissions.


49. Troubleshooting Publishing

ProblemLikely causeBetter approach
Version already existsImmutable version collisionIncrement version; do not reuse release number
Namespace conflictWrong owner/package scopeVerify package name and owner
Package published to wrong repoIncorrect source/destination metadataCheck repository/package config
npm publish goes to npmjs.org.npmrc/publishConfig incorrectExplicitly configure scope/registry
Maven deploy failsdistributionManagement/server ID mismatchMatch pom.xml server ID to settings.xml
Gradle credentials missingEnvironment variables unavailableInject GITHUB_ACTOR/GITHUB_TOKEN
NuGet source not foundnuget.config source mismatchValidate source URL/name
Container push deniedWrong namespace/token permissionValidate ghcr.io/OWNER/IMAGE and write access

Verify before publish

# npm
npm pack --dry-run

# Maven
mvn --batch-mode verify

# Gradle
./gradlew build

# .NET
dotnet pack --configuration Release

# Ruby
gem build your-package.gemspec

# Container
docker build -t local/test:verify .

50. Troubleshooting Installation and Dependency Resolution

Package not found

Possible causes:

  • Wrong package name/version
  • Wrong registry URL
  • Package is private and authentication is missing
  • Cross-repository access is missing
  • Client is querying the public registry first/only

Dependency resolution failure

Inspect the package manager’s source configuration before blaming GitHub Packages.

Examples:

npm config list
mvn help:effective-settings
./gradlew repositories

dotnet nuget list source
bundle config list

Version mismatch

Use explicit versions while diagnosing. Avoid floating ranges or mutable tags until basic connectivity is confirmed.


51. Container Registry Troubleshooting

Login

echo "$CR_PAT" | docker login ghcr.io -u "$GITHUB_USER" --password-stdin

If login fails, verify the PAT classic, scopes, SSO authorization, and account name.

Image not found

docker pull ghcr.io/acme/api:1.0.0

Check:

  • Owner capitalization/name
  • Package visibility
  • Tag existence
  • Package ACL
  • Authentication

Architecture mismatch

Symptom:

exec format error

Inspect image platforms:

docker buildx imagetools inspect ghcr.io/acme/api:1.0.0

Publish a multi-architecture image or deploy the matching platform.

Tag vs digest confusion

docker inspect --format='{{index .RepoDigests 0}}' ghcr.io/acme/api:1.0.0

Use digests when validating whether two environments run identical content.


52. CI/CD Troubleshooting

Workflow permission error

Inspect the job’s effective permissions:

permissions:
  contents: read
  packages: write

Do not assume repository defaults provide write access.

Pull-request security

Do not publish packages from untrusted fork PR code using privileged secrets/tokens. Split validation from trusted publish jobs.

Self-hosted runners

A compromised self-hosted runner may expose credentials, source, build outputs, or package tokens. Isolate runner groups and use ephemeral runners for high-value release workloads where practical.

Version collisions

If many parallel jobs publish the same version, only one can logically own that immutable release. Generate the version once and coordinate publishing.


53. Registry Migration

A registry migration is not just copying files. It includes metadata, credentials, consumers, automation, access, and rollback.

Migration phases

flowchart LR
    A[Inventory] --> B[Map namespaces/versions]
    B --> C[Design access]
    C --> D[Copy/publish artifacts]
    D --> E[Update CI publishers]
    E --> F[Update consumers]
    F --> G[Validate]
    G --> H[Freeze old registry]
    H --> I[Retire]

Inventory

Capture:

  • Packages
  • Versions/tags/digests
  • Owners
  • Visibility
  • Consumers
  • CI publishers
  • Credentials
  • Retention rules
  • Download/usage patterns

Consumer migration

Update:

  • Registry URL
  • Package source config
  • Credentials
  • Lockfiles if source metadata changes
  • CI workflows
  • Deployment pull secrets

Rollback

Keep the previous registry read-only for a defined period when business requirements allow. Do not delete source artifacts until consumers have been verified.


54. Legacy Docker Registry Migration

The legacy endpoint:

docker.pkg.github.com

has been replaced by:

ghcr.io

What changes

  • Image naming
  • Authentication configuration
  • Workflow registry endpoint
  • Package permission model
  • Repository association behavior
  • Consumer references

Example conceptual change

Old:

docker.pkg.github.com/acme/api/api:1.0.0

Modern:

ghcr.io/acme/api:1.0.0

Do not start new platform designs on the legacy Docker registry.


55. Observability, Activity, and Auditing

Package observability asks more than “is the registry up?”

Track:

  • Package inventory
  • Version count
  • Publication activity
  • Download activity where exposed
  • Package ownership
  • Repository links
  • Storage usage
  • Data transfer
  • Deletion events
  • Provenance
  • Deployment context

Audit questions

  • Who can publish this package?
  • Which workflow published version 2.7.3?
  • Which commit produced this digest?
  • Which repositories can consume it?
  • Is it deployed to production?
  • Does it have provenance?
  • Is an SBOM available?
  • Which vulnerable dependencies affect it?

Linked-artifact value

Linked artifacts help connect build, storage, provenance, and deployment metadata into an auditable view rather than forcing teams to reconstruct the story manually.


56. Organization Usage Reporting

At minimum, establish a periodic inventory report containing:

FieldWhy it matters
PackageAsset identity
TypeRegistry/ecosystem
VisibilityExposure risk
OwnerAccountability
Source repositoryTraceability
VersionsRetention pressure
Last updatedStaleness
Production useBusiness criticality
ProvenanceSupply-chain assurance
StorageCost/capacity

Simple REST inventory

gh api --paginate \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "/orgs/acme/packages?package_type=container&per_page=100" \
  --jq '.[] | {name,visibility,version_count,updated_at,html_url}'

Repeat per package type as needed.


57. Best Practices

Package design

Recommended:

  • Clear, stable names
  • Organization ownership for company artifacts
  • Semantic versions for libraries
  • Immutable release versions
  • Source repository linkage
  • License/description metadata
  • Installation documentation
  • Explicit package owner/team

Avoid:

  • Ambiguous names
  • User-owned business-critical packages
  • Version reuse
  • Packages without source traceability
  • Packages nobody owns

Security

Recommended:

  • Least privilege
  • GITHUB_TOKEN in Actions
  • Minimal PAT classic scopes
  • Credential rotation
  • Protected releases
  • Pinned third-party Actions for high-assurance workflows
  • Provenance
  • SBOM
  • Vulnerability scanning
  • Digest-based production deployments

Avoid:

  • Long-lived admin tokens
  • Shared PATs
  • Secrets in package config files
  • Publishing from untrusted PR jobs
  • Mutable-only production tags

CI/CD

Recommended:

  • Test before publish
  • Release/tag-driven production publishing
  • Reusable workflows
  • Build once, deploy many
  • Automated cleanup with dry run
  • Release metadata that captures digest/version

Avoid:

  • Rebuilding per environment
  • Manual production publishing as the normal path
  • Publishing every commit forever without retention

Organization governance

Recommended:

  • Team-based permissions
  • Default visibility rules
  • Naming policy
  • Versioning policy
  • Retention policy
  • Usage monitoring
  • Audit ownership

58. Common Anti-Patterns

Anti-patternWhy it is riskyBetter approach
Hardcoded tokenCredential leakSecret manager / GITHUB_TOKEN
One PAT shared by teamNo individual accountabilityTeam ACL + individual auth
Admin token for downloadsExcess privilegeRead-only token/access
Publish only from laptopWeak reproducibility/auditCI-first publishing
Reuse version numbersBreaks immutability/trustNew version per change
Only use latestCannot identify artifactSemVer + SHA + digest
No source linkWeak traceabilityConnect package to repo
Unlimited snapshotsStorage/cost noiseRetention policy
Public by accidentData exposurePrivate/internal default
No provenanceHard to verify originArtifact attestations
No SBOMPoor component visibilityGenerate SBOM
Rebuild for prodDifferent bytes than testedPromote same immutable artifact

59. Quick Reference Cheat Sheet

Registry endpoints

RegistryEndpoint / pattern
Containerghcr.io/OWNER/IMAGE
npmhttps://npm.pkg.github.com
Mavenhttps://maven.pkg.github.com/OWNER/REPOSITORY
GradleMaven-compatible GitHub Packages repository URL
NuGethttps://nuget.pkg.github.com/NAMESPACE/index.json
RubyGemshttps://rubygems.pkg.github.com/NAMESPACE

Token scopes

read:packages
write:packages
delete:packages

Workflow permissions

Read:

permissions:
  contents: read
  packages: read

Publish:

permissions:
  contents: read
  packages: write

Attest:

permissions:
  contents: read
  packages: write
  attestations: write
  id-token: write

Container commands

docker login ghcr.io
docker build -t ghcr.io/acme/api:1.0.0 .
docker push ghcr.io/acme/api:1.0.0
docker pull ghcr.io/acme/api:1.0.0

npm

npm publish
npm install @acme/pkg@1.0.0

Maven

mvn --batch-mode verify
mvn --batch-mode deploy

Gradle

./gradlew build
./gradlew publish

NuGet

dotnet pack --configuration Release
dotnet nuget push PACKAGE.nupkg --source github --api-key TOKEN

RubyGems

gem build package.gemspec
gem push --host https://rubygems.pkg.github.com/NAMESPACE package.gem

REST with GitHub CLI

gh api "/orgs/ORG/packages?package_type=container"
gh api "/orgs/ORG/packages/container/NAME/versions"

60. Beginner → Intermediate → Advanced Learning Map

LevelLearn and practice
BeginnerRegistry purpose, supported ecosystems, auth, publish/install, visibility, versions
IntermediateActions integration, cross-repo access, REST API, cleanup, billing, repository links
AdvancedBuild-once promotion, attestations, SBOM, linked artifacts, governance, migration, organization reporting

Recommended training order

  1. Publish/pull one public or private container.
  2. Publish one language package.
  3. Move publishing into GitHub Actions.
  4. Add a separate consumer repository.
  5. Add versioning and release triggers.
  6. Add provenance/SBOM.
  7. Add retention automation.
  8. Build an organization-level governance model.

61. Hands-On Exercises

Exercise 1 — Beginner: Publish a container

Objective: Publish and pull a versioned container from GHCR.

Tasks:

  1. Create a repository with a small Dockerfile.
  2. Build 1.0.0 locally.
  3. Authenticate to GHCR.
  4. Push the image.
  5. Open the package page.
  6. Pull the image on a clean machine/session.
  7. Record the digest.

Expected outcome: a package exists under the intended owner and can be pulled using the expected access model.

Exercise 2 — Intermediate: CI package publishing

Objective: Publish from GitHub Actions without storing a PAT.

Tasks:

  1. Create a release-triggered workflow.
  2. Set contents: read and packages: write.
  3. Authenticate with GITHUB_TOKEN.
  4. Run tests before publishing.
  5. Publish the package.
  6. Verify the package version from a second workflow.

Expected outcome: publishing is reproducible and tied to a GitHub workflow run.

Exercise 3 — Intermediate: Cross-repository consumption

Objective: Allow a consumer repository to read a private granular package.

Tasks:

  1. Publish a private package from repo A.
  2. Add repo B under package Actions access.
  3. Give repo B read access only.
  4. Set packages: read in repo B workflow.
  5. Install/pull the package.
  6. Remove access and observe the failure.
  7. Restore read access.

Expected outcome: students understand package ACLs separately from token scopes.

Exercise 4 — Advanced: Immutable promotion

Objective: Demonstrate build once, deploy many.

Tasks:

  1. Build and push a container.
  2. Capture digest.
  3. Deploy that digest to dev.
  4. Promote the same digest to stage.
  5. Verify both environments use identical digest.
  6. Promote to production.

Expected outcome: no environment performs a rebuild.

Exercise 5 — Security: Provenance and SBOM

Objective: Add supply-chain evidence.

Tasks:

  1. Add artifact-attestation permissions.
  2. Generate provenance after publishing.
  3. Generate an SPDX or CycloneDX SBOM.
  4. Attest the SBOM.
  5. Verify the artifact with GitHub CLI.
  6. Inspect the attestation output.

Expected outcome: students can prove where an artifact came from and inspect its component inventory.


62. Complete Practical Project — Organization Package Platform

Requirement

Design a small but production-oriented GitHub Packages platform for an organization named acme.

The organization has:

  • payments-api — containerized service
  • orders-api — containerized service
  • shared-types — npm package
  • payments-sdk — Maven package
  • Staging and production environments
  • GitHub Actions for CI/CD
  • A requirement for least privilege, traceability, reproducibility, and cleanup

Target architecture

flowchart TB
    subgraph Source
      P[payments-api]
      O[orders-api]
      N[shared-types]
      M[payments-sdk]
    end

    P --> PA[Payments CI]
    O --> OA[Orders CI]
    N --> NA[npm CI]
    M --> MA[Maven CI]

    PA --> GHCR[(GHCR)]
    OA --> GHCR
    NA --> NPM[(GitHub npm Registry)]
    MA --> MAVEN[(GitHub Maven Registry)]

    GHCR --> STG[Staging]
    GHCR --> PROD[Production]

    PA --> AT[Attestations]
    OA --> AT
    AT --> LA[Linked Artifacts]
    STG --> LA
    PROD --> LA

Design decisions

  1. Containers use ghcr.io/acme/<service>.
  2. Production image deployment uses digest references.
  3. npm package is organization scoped with granular permissions.
  4. Maven package remains repository scoped.
  5. Publishing happens only through trusted workflows.
  6. Workflows use GITHUB_TOKEN wherever the package access model allows it.
  7. Production publishing/deployment requires protected release controls.
  8. Production containers receive provenance attestations.
  9. SBOMs are generated for production images.
  10. Snapshot/dev package retention is automated.

Repository structure

payments-api/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── release.yml
├── src/
├── tests/
├── Dockerfile
└── README.md

Release workflow

name: Release container

on:
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  release:
    runs-on: ubuntu-latest
    environment: production-release
    permissions:
      contents: read
      packages: write
      attestations: write
      id-token: write

    steps:
      - uses: actions/checkout@v6

      - name: Test
        run: ./scripts/test.sh

      - name: Login
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=semver,pattern={{version}}
            type=sha

      - name: Build and push
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Attest
        uses: actions/attest@v4
        with:
          subject-name: ghcr.io/${{ github.repository }}
          subject-digest: ${{ steps.build.outputs.digest }}
          push-to-registry: true

      - name: Save digest
        run: echo '${{ steps.build.outputs.digest }}' > image-digest.txt

Staging deployment

Consume the digest produced by the release pipeline rather than rebuilding:

containers:
  - name: payments-api
    image: ghcr.io/acme/payments-api@sha256:RELEASE_DIGEST

Production promotion

The production change references the exact same digest that passed staging validation.

npm shared package access

shared-types is granted:

  • Write: owning platform/package-maintainer team
  • Read: payments-api and orders-api repositories through Actions access
  • Admin: small package-owner group

Consumer workflow:

permissions:
  contents: read
  packages: read

steps:
  - uses: actions/checkout@v6
  - uses: actions/setup-node@v7
    with:
      node-version: '20.x'
      registry-url: 'https://npm.pkg.github.com'
      scope: '@acme'
  - run: npm ci
    env:
      NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Cleanup policy

  • Production release versions: protected from automated deletion.
  • Release candidates: keep last 10.
  • Branch builds: keep 14 days.
  • Pull-request builds: keep 7 days after PR close.
  • Cleanup workflow first generates a report, then deletes only approved candidates.

Validation checklist

  • Package visible under correct organization/repository.
  • Source connection correct.
  • Producer has write; consumers have read only.
  • Workflow succeeds without long-lived PAT.
  • Release tags/version match policy.
  • Container digest recorded.
  • Staging and production use same digest.
  • Provenance verifies.
  • SBOM is retrievable/inspectable.
  • Cleanup excludes protected releases.
  • Audit owner is documented.

Improvement ideas

  • Pin all non-GitHub Actions to reviewed full commit SHAs.
  • Add reusable organization release workflows.
  • Add policy-as-code around naming and allowed registries.
  • Integrate linked artifact deployment metadata.
  • Add runtime risk context through supported integrations/API.
  • Export monthly package inventory and orphan-package report.

63. Interview and Knowledge-Check Questions

Conceptual

  1. What problem does GitHub Packages solve?
  2. How is a package registry different from GitHub Releases?
  3. Which GitHub Packages registries support granular permissions?
  4. Which registries are repository scoped?
  5. What is the difference between package visibility and package role?
  6. What is the difference between a container tag and digest?
  7. Why is GITHUB_TOKEN usually preferable to a PAT in GitHub Actions?
  8. Why should release versions be immutable?
  9. What is repository permission inheritance?
  10. What happens to granular packages when a connected repository is transferred?

Scenario based

  1. A workflow in repo B must install a private package published from repo A. How would you design access?
  2. A team deploys latest to production. What risks does that introduce?
  3. A package has 100 old development versions. How would you automate cleanup safely?
  4. An npm package publishes to npmjs.org instead of GitHub Packages. What would you inspect?
  5. A container works on AMD64 but fails on ARM64. How would you diagnose it?
  6. A developer can authenticate to GHCR but cannot push. What layers of authorization do you check?
  7. You must prove which commit produced a production image. Which GitHub features help?
  8. You are migrating from docker.pkg.github.com to ghcr.io. What must change beyond image copying?

Troubleshooting

  1. What is the difference between HTTP 401 and a permission-denied workflow failure?
  2. Why can a valid PAT still fail to download a package?
  3. How can repository transfer break package consumption?
  4. Why can rebuilding for production break provenance even when the Git commit is unchanged?
  5. When would REST be preferred over GraphQL for packages?
  6. What should be checked before deleting a public package?

64. FAQ

Do GitHub Packages support public and private packages?

Yes, with behavior depending on registry, permission model, and account/plan context. Granular registries can support independent visibility settings. Repository-scoped packages follow their repository.

Can I pull a public package without authentication?

For public GHCR container images, yes. Most other GitHub Packages registries still require authentication even when the package is public.

Can I use a fine-grained PAT to authenticate npm/Docker/Maven package clients?

GitHub’s package-client documentation continues to specify personal access token classic for GitHub Packages authentication. Some REST endpoints separately support GitHub App or fine-grained token types. Treat those as different authentication surfaces.

Should I use GITHUB_TOKEN or a PAT in Actions?

Prefer GITHUB_TOKEN when the package/repository access model supports the operation. It is automatically generated, short lived, and can be permission-scoped in the workflow.

Can one organization package be consumed by many repositories?

Yes for granular registries. Grant the required repositories read access through package settings/Actions access.

Are Maven and Gradle packages organization-scoped with independent ACLs?

No. Their GitHub Packages permission model is repository scoped.

Can I delete any public package?

Not always. GitHub documents a 5,000-download restriction for public package/version deletion; higher-download cases require GitHub Support.

How long can deleted packages be restored?

GitHub documents a 30-day restoration window, provided the required namespace/version conditions remain satisfied.

Is GHCR storage permanently free?

Do not assume that. As of September 2026, GitHub documents Container registry storage and bandwidth as currently free and says notice will be given before a policy change.

What is the safest container deployment reference?

An immutable digest, ideally connected to provenance and release metadata.

Do linked artifacts store my packages?

No. Linked artifacts store/aggregate metadata about artifacts, storage, provenance, and deployments. The artifact itself can live in GitHub Packages or an external registry.

What is the difference between an SBOM and an attestation?

An SBOM inventories components. An attestation is a signed statement about an artifact, such as its build provenance or an SBOM statement.

Should production packages be published from developer laptops?

Prefer controlled CI for normal production publishing because it improves reproducibility, auditing, permissions, and provenance.


65. Topic Coverage Map

The source curriculum contained 123 topic groups. This guide consolidates related topics into teachable chapters rather than repeating the same concepts under many headings.

Source topic groupsCovered primarily in
1–4Sections 1–4
5–10Sections 2–5
11–14Section 6
15–19Sections 7–9, 19
20–25Sections 15, 18
26–32Sections 8–9, 17, 44, 54
33Section 10
34Section 11
35Section 12
36Section 13
37Section 14
38–45Sections 16–17, 42–44
46–48Section 19, 46
49–51Sections 20–22
52–53Sections 23–24
54–56Sections 5, 18, 26–27
57–61Section 25
62–65Sections 28–29
66–68Section 30
69–74Sections 31–35
75–79Section 36
80–84Sections 37–41
85–89Sections 42–43
90–93Sections 44–45
94–97Section 46
98–103Sections 47–52
104–106Sections 53–54
107–109Sections 55–56
110–114Section 57
115Section 58
116–123Sections 16, 23–24, 31–36, 59, and references

66. Summary

You should now be able to:

  • Explain GitHub Packages and its registry architecture.
  • Distinguish granular from repository-scoped package permissions.
  • Publish and consume containers, npm, Maven, Gradle, NuGet, and RubyGems packages.
  • Use PAT classic safely for package clients.
  • Use GITHUB_TOKEN for package operations in GitHub Actions.
  • Design cross-repository package access.
  • Connect granular packages to source repositories.
  • Version and promote immutable artifacts.
  • Automate publication, cleanup, and reporting with Actions and the REST API.
  • Understand deletion/restoration rules.
  • Manage billing/storage considerations.
  • Generate provenance attestations and SBOMs.
  • Use dependency graph and Dependabot as part of supply-chain security.
  • Understand linked artifacts and production context.
  • Troubleshoot authentication, authorization, publishing, installation, container, and CI failures.
  • Plan registry and legacy Docker migrations.
  • Establish organization-level package governance.

Recommended next steps

  1. Build the complete practical project in a sandbox organization.
  2. Add a second consumer repository and test least-privilege access.
  3. Replace mutable deployment tags with digests.
  4. Add provenance and an SBOM.
  5. Create an inventory/cleanup workflow using the REST API.
  6. Document your organization’s naming, publication, retention, and visibility policy.
  7. Review your actual GitHub plan and enterprise settings before turning the examples into production standards.

67. Authoritative References

The guide was verified primarily against current GitHub documentation in September 2026.

GitHub Packages fundamentals

Registry documentation

GitHub Actions and automation

API, webhooks, and billing

Supply-chain security

GitHub features, limits, plan entitlements, action versions, and preview status can change. Re-check the authoritative documentation before production rollout or compliance sign-off.

Related Posts

GitHub Repository Settings — Complete Reference Guide & Tutorial

Last Verified: September 2026Platform: GitHub.com / GitHub Enterprise Cloud unless stated otherwiseAudience: Developers, DevOps engineers, repository administrators, security engineers, platform engineers, team leads, and trainers GitHub repository settings are the…

Read More

GitHub Organization Administration — Complete Reference Guide and Tutorial

Last Verified: September 2026Scope: GitHub.com organization administration, with GitHub Enterprise Cloud and GitHub Enterprise Server differences called out where they materially affect administration.Audience: GitHub organization owners, platform engineers, DevOps engineers,…

Read More

GitHub Projects — Complete Reference Guide & Tutorial

Scope: Current GitHub Projects / Projects v2, not Projects (classic).Audience: Developers, DevOps engineers, engineering managers, product managers, project administrators, platform teams, and GitHub organization owners.Last verified: 2026-09-26 against current GitHub documentation.Learning…

Read More

GitHub Actions — Complete Tutorial & Production Handbook

Audience: Developers, DevOps engineers, platform engineers, SREs, security engineers, technical leads, and architectsLevel: Beginner → Intermediate → Advanced → EnterpriseLast verified: 2026-09-26Primary source: GitHub Actions official documentation, plus the supplied 62-section…

Read More

Git: Git Branching & Merging – A Complete Tutorials

PRACTICAL TECHNICAL HANDBOOK / 19 SEPTEMBER 2026 Git Branching & Merging Branch types, integration choices, conflicts and safe recovery Understand what Git changes, choose the right method,…

Read More

AWS Elastic IP Cross-Account / Cross-Organization Transfer Runbook

Use case: AWS Organization A / Account A → AWS Organization B / Account BObjective: Retain the exact same public IPv4 address while changing AWS account ownership….

Read More