Docker.Registry.DotNet
October 6, 2025 · View on GitHub
.NET (C#) Client Library for interacting with the Docker Registry API (v2).
Table of Contents
- Installation
- Quick Start
- Authentication
- Core Operations
- Real-World Scenarios
- Registry-Specific Examples
- Configuration & Advanced Topics
- Error Handling
- Troubleshooting
- API Reference
- Migration Guide
- Contributing
- License
Installation
Package Manager (Visual Studio)
PM> Install-Package Docker.Registry.DotNet
.NET CLI
dotnet add package Docker.Registry.DotNet
Package Reference (.csproj)
<PackageReference Include="Docker.Registry.DotNet" Version="2.0.0" />
Supported Frameworks
- .NET Standard 2.0
- .NET 5.0, 6.0, 7.0, 8.0+
Quick Start
Local Registry (No Authentication)
using Docker.Registry.DotNet;
var configuration = new RegistryClientConfiguration("http://localhost:5000");
using var client = configuration.CreateClient();
// Get catalog of repositories
var catalog = await client.Catalog.GetCatalog();
foreach (var repo in catalog.Repositories)
{
Console.WriteLine($"Repository: {repo}");
// List tags for each repository
var tags = await client.Tags.ListTags(repo);
foreach (var tag in tags.Tags)
{
Console.WriteLine($" Tag: {tag}");
}
}
Remote Registry with Authentication
var configuration = new RegistryClientConfiguration("https://registry.mycompany.com");
configuration.UsePasswordOAuthAuthentication("username", "password");
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
Docker Hub
var configuration = new RegistryClientConfiguration("https://hub.docker.com");
using var client = configuration.CreateClient();
// List tags for a specific repository
var tags = await client.Repository.ListRepositoryTags("grafana", "loki-docker-driver");
foreach (var tag in tags.Tags)
{
Console.WriteLine($"{tag.Name} - Last Updated: {tag.LastUpdated}");
}
Authentication
Docker.Registry.DotNet supports multiple authentication methods depending on your registry configuration.
Anonymous OAuth Authentication (Default)
Use this for registries that allow anonymous access or require OAuth tokens without credentials.
var configuration = new RegistryClientConfiguration("https://registry-1.docker.io");
configuration.UseAnonymousOAuthAuthentication();
using var client = configuration.CreateClient();
Basic Authentication
Use this for registries that support HTTP Basic Authentication.
var configuration = new RegistryClientConfiguration("https://registry.mycompany.com");
configuration.UseBasicAuthentication("username", "password");
using var client = configuration.CreateClient();
Password OAuth Authentication
Use this for registries that require OAuth token-based authentication (most common for private registries).
var configuration = new RegistryClientConfiguration("https://registry.mycompany.com");
configuration.UsePasswordOAuthAuthentication("username", "password");
using var client = configuration.CreateClient();
Authentication for Different Registries
| Registry | Authentication Method |
|---|---|
| Docker Hub | UseAnonymousOAuthAuthentication() (public) or UsePasswordOAuthAuthentication() (private) |
| Azure Container Registry (ACR) | UseBasicAuthentication() with username and access token |
| Amazon ECR | Use AWS credentials to obtain a token, then UseBasicAuthentication("AWS", token) |
| Harbor | UseBasicAuthentication() or UsePasswordOAuthAuthentication() |
| GitLab Container Registry | UseBasicAuthentication() with personal access token |
| Local Registry | Often no authentication or UseBasicAuthentication() |
Core Operations
Catalog Operations
Get All Repositories
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
foreach (var repository in catalog.Repositories)
{
Console.WriteLine(repository);
}
Get Repositories with Pagination
var parameters = new CatalogParameters
{
Number = 100 // Number of repositories per page
};
var catalog = await client.Catalog.GetCatalog(parameters);
// Check if there are more results
if (!string.IsNullOrEmpty(catalog.Link))
{
Console.WriteLine($"More results available at: {catalog.Link}");
}
Iterate Through All Repositories (Paginated)
var allRepositories = new List<string>();
CatalogParameters? parameters = new CatalogParameters { Number = 100 };
do
{
var catalog = await client.Catalog.GetCatalog(parameters);
allRepositories.AddRange(catalog.Repositories);
// Check if there's a next page
if (string.IsNullOrEmpty(catalog.Last))
break;
parameters = new CatalogParameters
{
Number = 100,
Last = catalog.Last // Use last repository as marker
};
} while (true);
Console.WriteLine($"Total repositories: {allRepositories.Count}");
Tag Operations
List Tags for a Repository
var tags = await client.Tags.ListTags("myapp");
foreach (var tag in tags.Tags)
{
Console.WriteLine(tag);
}
List Tags with Pagination
var parameters = new ListTagsParameters
{
Number = 50 // Tags per page
};
var tags = await client.Tags.ListTags("myapp", parameters);
List Tags by Digest (Get All Tags for a Specific Image)
This is useful to find all tags pointing to the same image digest.
var tagsByDigest = await client.Tags.ListTagsByDigests("myapp");
foreach (var digest in tagsByDigest.Manifests)
{
Console.WriteLine($"Digest: {digest.Digest}");
Console.WriteLine($"Tags: {string.Join(", ", digest.Tags)}");
Console.WriteLine($"MediaType: {digest.MediaType}");
Console.WriteLine($"Size: {digest.Size} bytes");
Console.WriteLine();
}
Manifest Operations
Get Image Manifest
var manifestResult = await client.Manifest.GetManifest("myapp", "latest");
Console.WriteLine($"MediaType: {manifestResult.MediaType}");
Console.WriteLine($"Digest: {manifestResult.DockerContentDigest}");
// Access manifest details
if (manifestResult.Manifest is ImageManifest2_2 manifest)
{
Console.WriteLine($"Architecture: {manifest.Config.Platform?.Architecture}");
Console.WriteLine($"OS: {manifest.Config.Platform?.OS}");
// Access layers
foreach (var layer in manifest.Layers)
{
Console.WriteLine($"Layer: {layer.Digest}, Size: {layer.Size} bytes");
}
}
Get Full Manifest with History
To access complete manifest information including history:
var manifestResult = await client.Manifest.GetManifest("myapp", "latest");
if (manifestResult.Manifest is ImageManifest2_2 manifest)
{
// Get the config blob which contains history
var configBlob = await client.Blobs.GetBlob("myapp", manifest.Config.Digest);
using var reader = new StreamReader(configBlob.Stream);
var configJson = await reader.ReadToEndAsync();
// Parse config JSON to access history
var config = JsonConvert.DeserializeObject<ImageConfig>(configJson);
foreach (var historyEntry in config.History)
{
Console.WriteLine($"Created: {historyEntry.Created}");
Console.WriteLine($"Command: {historyEntry.CreatedBy}");
Console.WriteLine();
}
}
Get Raw Manifest (as JSON string)
var rawManifest = await client.Manifest.GetManifestRaw("myapp", "latest");
Console.WriteLine(rawManifest);
Get Manifest Digest
var digest = await client.Manifest.GetDigest("myapp", "latest");
Console.WriteLine($"Digest: {digest}");
Push/Upload a Manifest
var manifest = new ImageManifest2_2
{
SchemaVersion = 2,
MediaType = "application/vnd.docker.distribution.manifest.v2+json",
Config = new Descriptor
{
MediaType = "application/vnd.docker.container.image.v1+json",
Digest = configDigest,
Size = configSize
},
Layers = new List<Descriptor>
{
new Descriptor
{
MediaType = "application/vnd.docker.image.rootfs.diff.tar.gzip",
Digest = layerDigest,
Size = layerSize
}
}
};
var response = await client.Manifest.PutManifest("myapp", "v1.0.0", manifest);
Console.WriteLine($"Manifest uploaded with digest: {response.DockerContentDigest}");
Delete a Manifest
// Delete by digest (recommended to avoid deleting wrong manifest)
var digest = await client.Manifest.GetDigest("myapp", "latest");
await client.Manifest.DeleteManifest("myapp", digest);
Console.WriteLine($"Deleted manifest with digest: {digest}");
Blob Operations
Download a Blob (Layer)
var manifestResult = await client.Manifest.GetManifest("myapp", "latest");
if (manifestResult.Manifest is ImageManifest2_2 manifest)
{
var firstLayer = manifest.Layers.First();
var blob = await client.Blobs.GetBlob("myapp", firstLayer.Digest);
Console.WriteLine($"Content Type: {blob.ContentType}");
Console.WriteLine($"Digest: {blob.DockerContentDigest}");
// Save blob to file
using var fileStream = File.Create($"layer-{firstLayer.Digest}.tar.gz");
await blob.Stream.CopyToAsync(fileStream);
}
Check if Blob Exists
var exists = await client.Blobs.BlobExists("myapp", "sha256:abc123...");
if (exists)
{
Console.WriteLine("Blob exists in registry");
}
Delete a Blob
await client.Blobs.DeleteBlob("myapp", "sha256:abc123...");
Console.WriteLine("Blob deleted");
Blob Upload Operations
Monolithic Upload (Simple Upload)
For smaller blobs, use a monolithic upload:
using var fileStream = File.OpenRead("my-layer.tar.gz");
// Calculate digest
using var sha256 = SHA256.Create();
var hash = await sha256.ComputeHashAsync(fileStream);
var digest = $"sha256:{BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant()}";
fileStream.Position = 0;
// Start upload
var upload = await client.BlobUploads.StartUploadBlob("myapp");
// Complete upload in one request
var imageDigest = new ImageDigest(digest);
var result = await client.BlobUploads.MonolithicUploadBlob(
upload,
imageDigest,
fileStream
);
Console.WriteLine($"Uploaded blob: {result.DockerContentDigest}");
Chunked Upload (For Large Files)
For larger blobs, use chunked uploads with resumable capability:
using var fileStream = File.OpenRead("large-layer.tar.gz");
// Calculate final digest
var digest = CalculateSHA256(fileStream);
fileStream.Position = 0;
// Start upload session
var upload = await client.BlobUploads.StartUploadBlob("myapp");
const int chunkSize = 5 * 1024 * 1024; // 5 MB chunks
var buffer = new byte[chunkSize];
long position = 0;
while (position < fileStream.Length)
{
var bytesRead = await fileStream.ReadAsync(buffer, 0, chunkSize);
using var chunkStream = new MemoryStream(buffer, 0, bytesRead);
var from = position;
var to = position + bytesRead - 1;
// Upload chunk
upload = await client.BlobUploads.UploadBlobChunk(
upload,
chunkStream,
from,
to
);
position += bytesRead;
Console.WriteLine($"Uploaded {position}/{fileStream.Length} bytes");
}
// Complete the upload
var imageDigest = new ImageDigest(digest);
var result = await client.BlobUploads.CompleteBlobUpload(upload, imageDigest);
Console.WriteLine($"Upload complete: {result.DockerContentDigest}");
Mount a Blob from Another Repository
Copy a blob from one repository to another without re-uploading:
var parameters = new MountParameters
{
From = "source-repo",
Mount = "sha256:abc123..." // Blob digest to mount
};
var result = await client.BlobUploads.MountBlob("destination-repo", parameters);
if (result.DockerUploadUuid == null)
{
Console.WriteLine("Blob mounted successfully!");
}
else
{
Console.WriteLine("Mount not supported, need to upload blob");
}
Cancel an Upload
var upload = await client.BlobUploads.StartUploadBlob("myapp");
// ... something goes wrong ...
await client.BlobUploads.CancelBlobUpload("myapp", upload.UploadUuid);
Console.WriteLine("Upload cancelled");
Real-World Scenarios
Copy an Image Between Registries
var sourceConfig = new RegistryClientConfiguration("https://source-registry.com");
sourceConfig.UsePasswordOAuthAuthentication("user1", "pass1");
var destConfig = new RegistryClientConfiguration("https://dest-registry.com");
destConfig.UsePasswordOAuthAuthentication("user2", "pass2");
using var sourceClient = sourceConfig.CreateClient();
using var destClient = destConfig.CreateClient();
// Get manifest from source
var manifest = await sourceClient.Manifest.GetManifest("myapp", "v1.0");
if (manifest.Manifest is ImageManifest2_2 imageManifest)
{
// Copy config blob
var configBlob = await sourceClient.Blobs.GetBlob("myapp", imageManifest.Config.Digest);
var configUpload = await destClient.BlobUploads.StartUploadBlob("myapp");
await destClient.BlobUploads.MonolithicUploadBlob(
configUpload,
new ImageDigest(imageManifest.Config.Digest),
configBlob.Stream
);
// Copy each layer
foreach (var layer in imageManifest.Layers)
{
var layerBlob = await sourceClient.Blobs.GetBlob("myapp", layer.Digest);
var layerUpload = await destClient.BlobUploads.StartUploadBlob("myapp");
await destClient.BlobUploads.MonolithicUploadBlob(
layerUpload,
new ImageDigest(layer.Digest),
layerBlob.Stream
);
}
// Push manifest to destination
await destClient.Manifest.PutManifest("myapp", "v1.0", imageManifest);
Console.WriteLine("Image copied successfully!");
}
List All Tags Across All Repositories
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
foreach (var repo in catalog.Repositories)
{
var tags = await client.Tags.ListTags(repo);
foreach (var tag in tags.Tags)
{
Console.WriteLine($"{repo}:{tag}");
}
}
Download All Layers of an Image
using var client = configuration.CreateClient();
var manifest = await client.Manifest.GetManifest("myapp", "latest");
if (manifest.Manifest is ImageManifest2_2 imageManifest)
{
Directory.CreateDirectory("layers");
int layerIndex = 0;
foreach (var layer in imageManifest.Layers)
{
Console.WriteLine($"Downloading layer {layerIndex}: {layer.Digest}");
var blob = await client.Blobs.GetBlob("myapp", layer.Digest);
var fileName = $"layers/layer-{layerIndex:D3}.tar.gz";
using var fileStream = File.Create(fileName);
await blob.Stream.CopyToAsync(fileStream);
Console.WriteLine($"Saved to {fileName} ({layer.Size} bytes)");
layerIndex++;
}
}
Clean Up Old Image Tags
using var client = configuration.CreateClient();
var cutoffDate = DateTime.UtcNow.AddMonths(-6);
var tagsToDelete = new List<string>();
var tagsByDigest = await client.Tags.ListTagsByDigests("myapp");
foreach (var manifest in tagsByDigest.Manifests)
{
// Note: You'd need to get the actual creation date from the manifest
// This is a simplified example
foreach (var tag in manifest.Tags)
{
if (tag.StartsWith("temp-") || tag.Contains("-old"))
{
tagsToDelete.Add(tag);
}
}
}
foreach (var tag in tagsToDelete)
{
var digest = await client.Manifest.GetDigest("myapp", tag);
if (digest != null)
{
await client.Manifest.DeleteManifest("myapp", digest);
Console.WriteLine($"Deleted tag: {tag}");
}
}
Registry-Specific Examples
Docker Hub
Public Repository
var configuration = new RegistryClientConfiguration("https://hub.docker.com");
using var client = configuration.CreateClient();
var tags = await client.Repository.ListRepositoryTags("library", "nginx");
Private Repository
var configuration = new RegistryClientConfiguration("https://proget.mycompany.com");
configuration.UsePasswordOAuthAuthentication("username", "password");
using var client = configuration.CreateClient();
var tags = await client.Tags.ListTags("myapp");
Azure Container Registry (ACR)
var configuration = new RegistryClientConfiguration("https://myregistry.azurecr.io");
// Use the admin username and password, or a service principal
configuration.UseBasicAuthentication("username", "password");
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
Amazon ECR
// First, get an authorization token using AWS SDK
// var ecrClient = new AmazonECRClient();
// var authResponse = await ecrClient.GetAuthorizationTokenAsync(...);
// var token = DecodeBase64(authResponse.AuthorizationData[0].AuthorizationToken);
var configuration = new RegistryClientConfiguration("https://123456789012.dkr.ecr.us-east-1.amazonaws.com");
configuration.UseBasicAuthentication("AWS", token);
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
Harbor
var configuration = new RegistryClientConfiguration("https://harbor.mycompany.com");
configuration.UseBasicAuthentication("admin", "Harbor12345");
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
GitLab Container Registry
var configuration = new RegistryClientConfiguration("https://registry.gitlab.com");
// Use personal access token or deploy token
configuration.UseBasicAuthentication("gitlab-ci-token", "your-token");
using var client = configuration.CreateClient();
var tags = await client.Tags.ListTags("mygroup/myproject");
Local Development Registry
// No authentication typically required
var configuration = new RegistryClientConfiguration("http://localhost:5000");
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
Configuration & Advanced Topics
Custom HttpClient
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromMinutes(10)
};
var configuration = new RegistryClientConfiguration("https://registry.mycompany.com");
// Note: The library creates its own HttpClient internally
// For advanced scenarios, consider creating a custom HttpMessageHandler
Working with Self-Signed Certificates
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
{
// WARNING: Only use this in development/testing environments
return true; // Accept all certificates
}
};
// For production, properly validate the certificate
// or add it to your system's trusted certificate store
ActivitySource Support (Telemetry/Tracing)
The library includes built-in ActivitySource support for distributed tracing:
using System.Diagnostics;
// Create an ActivityListener to capture traces
var listener = new ActivityListener
{
ShouldListenTo = source => source.Name == "Docker.Registry.DotNet",
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData,
ActivityStarted = activity => Console.WriteLine($"Started: {activity.DisplayName}"),
ActivityStopped = activity => Console.WriteLine($"Stopped: {activity.DisplayName} ({activity.Duration})")
};
ActivitySource.AddActivityListener(listener);
// Now all registry operations will be traced
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
Proxy Configuration
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://proxy.company.com:8080"),
UseProxy = true
};
// Use the handler with your configuration
Error Handling
Handling Unauthorized Exceptions
using Docker.Registry.DotNet.Domain.Registry;
try
{
var catalog = await client.Catalog.GetCatalog();
}
catch (UnauthorizedApiException ex)
{
Console.WriteLine("Authentication failed!");
Console.WriteLine($"Status Code: {ex.StatusCode}");
// Inspect WWW-Authenticate headers for more details
foreach (var header in ex.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}
}
Handling General API Exceptions
try
{
var manifest = await client.Manifest.GetManifest("myapp", "nonexistent-tag");
}
catch (DockerApiException ex)
{
Console.WriteLine($"API Error: {ex.StatusCode}");
Console.WriteLine($"Message: {ex.Message}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Network error: {ex.Message}");
}
Retry Logic with Polly
using Polly;
var retryPolicy = Policy
.Handle<HttpRequestException>()
.Or<TaskCanceledException>()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
await retryPolicy.ExecuteAsync(async () =>
{
var catalog = await client.Catalog.GetCatalog();
});
Proper Disposal
// Use 'using' statement to ensure proper disposal
using (var client = configuration.CreateClient())
{
var catalog = await client.Catalog.GetCatalog();
// Client is disposed here
}
// Or with 'using' declaration (C# 8.0+)
using var client = configuration.CreateClient();
var catalog = await client.Catalog.GetCatalog();
// Client is disposed at end of scope
Troubleshooting
Issue: "Unable to read manifest with provenance enabled image"
Problem: Images built with Docker BuildKit's provenance feature (--provenance true) may fail to retrieve manifests.
Cause: BuildKit creates manifests with OCI media types that may not be fully supported in older versions.
Solution:
-
Build images with
--provenance falsefor compatibility:docker build --provenance false -t myimage:latest . -
Or ensure you're using the latest version of Docker.Registry.DotNet which includes improved OCI support.
Issue: "405 Method Not Allowed" During Authentication
Problem: Authentication fails with a 405 error.
Cause: The registry may not support the authentication method being used.
Solution:
-
Try different authentication methods:
// Try Basic Authentication instead of OAuth configuration.UseBasicAuthentication("username", "password"); // Or try Anonymous OAuth configuration.UseAnonymousOAuthAuthentication(); -
Verify the registry supports the Docker Registry API v2.
Issue: "401 Unauthorized" Errors
Problem: Getting 401 errors even with correct credentials.
Cause: Various authentication issues.
Solutions:
- Verify credentials are correct
- Check if user has permissions for the repository
- For Docker Hub, use your username (not email) and an access token (not password)
- For cloud registries (ACR, ECR, GCR), ensure you're using the correct authentication method
Issue: SSL/TLS Certificate Errors
Problem: The SSL connection could not be established
Solution:
// For development only - accept self-signed certificates
// DO NOT use in production!
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
For production, add the certificate to your system's trusted certificate store.
Issue: Timeout on Large Image Operations
Problem: Operations timeout when working with large images.
Solution: Increase timeout and use chunked uploads:
// The library handles timeouts internally
// For very large operations, use chunked uploads instead of monolithic uploads
Issue: "Manifest Not Found" for Valid Tags
Problem: Getting 404 errors for tags that exist.
Cause: Registry may be using a different API endpoint or authentication scope.
Solution:
- Verify the repository name format (some registries use
namespace/repository) - Check authentication scopes include the repository
- Try using
Repository.ListRepositoryTags()for Docker Hub instead ofTags.ListTags()
API Reference
The IRegistryClient interface provides access to all registry operations:
Available Operations
client.Manifest
Manifest operations for working with image manifests.
GetManifest()- Get an image manifestGetManifestRaw()- Get raw manifest JSONGetDigest()- Get manifest digestPutManifest()- Upload/push a manifestDeleteManifest()- Delete a manifest
client.Catalog
Catalog operations for listing repositories.
GetCatalog()- Get list of repositories
client.Blobs
Blob operations for downloading image layers.
GetBlob()- Download a blob/layerBlobExists()- Check if a blob existsDeleteBlob()- Delete a blob
client.BlobUploads
Blob upload operations for pushing image layers.
StartUploadBlob()- Start an upload sessionMonolithicUploadBlob()- Upload blob in one requestUploadBlobChunk()- Upload a chunk (for resumable uploads)CompleteBlobUpload()- Complete a chunked uploadCancelBlobUpload()- Cancel an uploadMountBlob()- Mount blob from another repositoryGetBlobUploadStatus()- Get status of resumable upload
client.Tags
Tag operations for working with image tags.
ListTags()- List tags for a repositoryListTagsByDigests()- List tags grouped by digest (shows all tags for each image)
client.Repository
Docker Hub specific repository operations.
ListRepositoryTags()- List tags for a Docker Hub repository
client.System
System operations.
- Operations for registry health checks and version info
Migration Guide
Migrating from v1.x to v2.x
Version 2.0 introduced several breaking changes for a more modern .NET API:
Method Names Changed (Removed "Async" Suffix)
v1.x:
var catalog = await client.Catalog.GetCatalogAsync();
var tags = await client.Tags.ListImageTagsAsync("myapp", new ListImageTagsParameters());
v2.x:
var catalog = await client.Catalog.GetCatalog();
var tags = await client.Tags.ListTags("myapp", new ListTagsParameters());
Note: Legacy methods with Async suffix are still available but marked as [Obsolete] to help with migration.
Configuration Builder Pattern
v1.x:
var configuration = new RegistryClientConfiguration("https://registry.com");
var client = new RegistryClient(configuration);
v2.x (Recommended):
var configuration = new RegistryClientConfiguration("https://registry.com");
configuration.UsePasswordOAuthAuthentication("user", "pass");
using var client = configuration.CreateClient();
Authentication Setup Simplified
Authentication configuration is now done through extension methods:
// Basic Authentication
configuration.UseBasicAuthentication("username", "password");
// Password OAuth
configuration.UsePasswordOAuthAuthentication("username", "password");
// Anonymous OAuth
configuration.UseAnonymousOAuthAuthentication();
Framework Support
v2.x adds direct support for:
- .NET 5.0
- .NET 6.0
- .NET 7.0
- .NET 8.0
While maintaining .NET Standard 2.0 support for maximum compatibility.
New Features in v2.x
- ActivitySource support for distributed tracing
- Improved error handling
- Docker Hub registry support via
Repositoryoperations ListTagsByDigests()to get all tags for specific images- Better nullability annotations
- Improved async/await patterns
Contributing
We welcome contributions!
How to Contribute
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
Reporting Issues
If you find a bug or have a feature request, please open an issue on GitHub.
License
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Copyright © Rich Quackenbush, Jaben Cargman and the Docker.Registry.DotNet Contributors 2017-2024
Questions? Check out the sample projects or open an issue!