Skip to main content
The Docker Hub provider tracks container image tags from Docker Hub using the Docker Hub API v2.

Package Name Format

Docker images can be specified in two formats: Official images:
packages:
  - name: node
    provider: docker
Official images are automatically prefixed with the library namespace internally. User/organization images:
packages:
  - name: grafana/grafana
    provider: docker
Invalid formats:
  • “ (empty string)
  • user/image/extra - Too many path segments
  • registry.com/user/image - Registry prefixes not supported

Name Parsing

The provider parses Docker image names to extract namespace and image name:
const parseDockerName = (name: string): { namespace: string; imageName: string } | null => {
  const parts = name.split("/");
  
  if (parts.length === 1 && parts[0]) {
    // Official image: "node" -> { namespace: "library", imageName: "node" }
    return { namespace: "library", imageName: parts[0] };
  } else if (parts.length === 2 && parts[0] && parts[1]) {
    // User image: "grafana/grafana" -> { namespace: "grafana", imageName: "grafana" }
    return { namespace: parts[0], imageName: parts[1] };
  }
  
  return null;  // Invalid format
};
Source: server/providers/docker/index.ts:11-26

Configuration Options

The Docker Hub provider supports the following extra options:

tags

Type: string[]
Default: ["latest", "slim", "alpine"]
Specific image tags to track.
packages:
  - name: node
    provider: docker
    extra:
      tags: ["latest", "lts", "alpine"]
Each tag is fetched individually from the Docker Hub API. If a tag doesn’t exist, it’s silently skipped.

Complete Example

packages:
  - name: postgres
    provider: docker
    extra:
      tags: ["latest", "alpine", "16-alpine"]

API Endpoints

The Docker Hub provider uses the Docker Hub v2 API:

Get Repository Info

GET https://hub.docker.com/v2/repositories/{namespace}/{imageName}/
Example:
GET https://hub.docker.com/v2/repositories/library/node/
Response fields used:
  • user - Repository owner username
  • name - Image name
  • namespace - Repository namespace
  • description - Short description
  • star_count - Number of stars
  • pull_count - Total pull count
  • is_private - Whether repository is private
  • last_updated - Last update timestamp
Source: server/providers/docker/types.ts:43-63

Get Specific Tag

GET https://hub.docker.com/v2/namespaces/{namespace}/repositories/{imageName}/tags/{tag}
Example:
GET https://hub.docker.com/v2/namespaces/library/repositories/node/tags/latest
Response fields used:
  • name - Tag name
  • digest - Image digest hash
  • last_updated - Last update timestamp
  • tag_last_pushed - Last push timestamp
  • full_size - Total size in bytes
  • images - Array of platform-specific images
Source: server/providers/docker/types.ts:17-34

Package Output Format

The Docker Hub provider returns packages in this format: Official image:
{
  name: "node",
  owner: undefined,           // library namespace is hidden
  providerName: "docker",
  url: "https://hub.docker.com/r/library/node",
  description: "Node.js is a JavaScript-based platform...",
  stars: 15234,
  releases: [
    {
      name: "latest",
      version: "latest",
      tag: "latest",
      createdAt: "2024-01-15T10:30:00Z",
      url: "https://hub.docker.com/r/library/node/tags/latest"
    }
  ]
}
User image:
{
  name: "grafana",
  owner: "grafana",
  providerName: "docker",
  url: "https://hub.docker.com/r/grafana/grafana",
  description: "Grafana monitoring platform",
  stars: 5678,
  releases: [...]
}

Tag Fetching Strategy

Tags are fetched individually with retry logic and error handling:
const tagResults = yield* Effect.all(
  tagsToGet
    .filter((t): t is string => isString(t))
    .map((tagName) =>
      dockerClient.getTag(namespace, imageName, tagName).pipe(
        Effect.retry({
          times: 2,
          schedule: Schedule.exponential(Duration.seconds(1), 1.5),
          while(error) {
            return error._tag === "Network";
          },
        }),
        Effect.either,
      ),
    ),
  { concurrency: 5 },
);
Source: server/providers/docker/index.ts:69-85 Features:
  • Parallel fetching - Up to 5 tags fetched concurrently
  • Retry logic - Network errors retried up to 2 times with exponential backoff
  • Error isolation - Failed tags don’t prevent other tags from loading
  • Digest validation - Tags without valid digests are filtered out

Release Filtering

Only valid tags are included in releases:
const releases = tagResults
  .filter((res): res is Either.Right<never, DockerTag> => {
    return (
      Either.isRight(res) &&
      !!res.right.digest  // Must have a valid digest
    );
  })
  .map((result) => {
    const tag = result.right;
    return PackageRelease.make({
      version: tag.name,
      name: tag.name,
      tag: tag.name,
      createdAt: tag.tag_last_pushed || tag.last_updated,
      url: `${DOCKER_HUB_URL}/${namespace}/${imageName}/tags/${tag.name}`,
    });
  });
Source: server/providers/docker/index.ts:88-106

URL Generation

Docker Hub URLs are constructed differently for repositories and tags: Repository URL:
https://hub.docker.com/r/{namespace}/{imageName}
Tag URL:
https://hub.docker.com/r/{namespace}/{imageName}/tags/{tag}
Examples:
  • Repository: https://hub.docker.com/r/library/node
  • Tag: https://hub.docker.com/r/library/node/tags/latest
Source: server/providers/docker/index.ts:9 and index.ts:104

Error Handling

Invalid Package Name

Occurs when:
  • Name is empty or whitespace only
  • Name has invalid format (too many or too few segments)
new InvalidPackageNameError({ name, provider: "docker" })
Source: server/providers/docker/index.ts:39 and index.ts:45

Package Not Found (404)

Occurs when:
  • Repository doesn’t exist
  • Repository is private and not accessible
  • Namespace or image name is incorrect
new PackageNotFoundError({ 
  name: `${namespace}/${name}`, 
  provider: "docker" 
})
For individual tags:
new PackageNotFoundError({ 
  name: `${namespace}/${name}:${tag}`, 
  provider: "docker" 
})
Source: server/providers/docker/client.ts:42 and client.ts:84

Network Error

Occurs when:
  • Docker Hub API is unreachable
  • Request times out
  • Other HTTP errors (non-404)
new NetworkError({
  name: `${namespace}/${name}`,
  provider: "docker",
  reason: error.message
})
Source: server/providers/docker/client.ts:44-48

Implementation Details

Client Layer

The Docker client (DockerClient) provides three methods:
class DockerClient {
  getRepository(namespace: string, name: string): Effect<DockerRepository, ProviderError>
  getTags(namespace: string, name: string, pageSize?: number): Effect<DockerTagsResponse, ProviderError>
  getTag(namespace: string, name: string, tag: string): Effect<DockerTag, ProviderError>
}
Source: server/providers/docker/client.ts:11-28

Type Definitions

DockerTag:
{
  creator: number;
  id: number;
  images: DockerImage[];
  last_updated: string;
  last_updater: number;
  last_updater_username: string;
  name: string;
  repository: number;
  full_size: number;
  v2: boolean;
  tag_status: string;
  tag_last_pulled: string | null;
  tag_last_pushed: string;
  media_type: string;
  content_type: string;
  digest: string;
}
Source: server/providers/docker/types.ts:17-34 DockerRepository:
{
  user: string;
  name: string;
  namespace: string;
  repository_type: string;
  status: number;
  status_description: string;
  description: string;
  is_private: boolean;
  is_automated: boolean;
  star_count: number;
  pull_count: number;
  last_updated: string;
  date_registered: string;
  collaborator_count: number;
  affiliation: string | null;
  hub_user: string;
  has_starred: boolean;
  full_description: string;
}
Source: server/providers/docker/types.ts:43-63 DockerImage (Platform-specific):
{
  architecture: string;      // "amd64", "arm64", etc.
  os: string;                // "linux", "windows"
  variant: string | null;    // "v7", "v8" for ARM
  digest: string;
  size: number;
  status: string;
  last_pulled: string | null;
  last_pushed: string | null;
}
Source: server/providers/docker/types.ts:3-15

Provider Info

export const DockerProviderInfo = new ProviderInfo({
  id: "docker",
  name: "Docker Hub",
  homepage: "https://hub.docker.com",
  icon: "devicon:docker",
  extraSchema: Schema.Struct({
    tags: Schema.Array(Schema.String).pipe(Schema.optional),
  }),
  extraDefaults: {
    tags: ["latest", "slim", "alpine"],
  },
});
Source: shared/providers/docker.ts:5-16

Retry and Resilience

The Docker provider implements exponential backoff retry for network errors:
  • Retry count: 2 times
  • Initial delay: 1 second
  • Backoff multiplier: 1.5x
  • Only retries: Network errors (not 404s)
This helps handle transient Docker Hub API issues without failing the entire request. Source: server/providers/docker/index.ts:74-80

Concurrency Control

Tag fetching is limited to 5 concurrent requests to avoid overwhelming the Docker Hub API:
Effect.all(tagRequests, { concurrency: 5 })
Source: server/providers/docker/index.ts:84

Caching

Docker Hub provider requests are cached using the standard Shipped cache:
  • Cache key includes: namespace, image name, and configuration hash
  • Repository info and individual tags are cached separately
  • Tag requests benefit from individual caching (useful when tags list changes)

Version History

Current version: 1 Source: server/providers/docker/index.ts:33

Build docs developers (and LLMs) love