---
name: update-container-images
description: Updates Docker container image tags used by Aspire hosting integrations. Queries registries for newer tags, uses LLM to determine version-compatible updates, and applies changes. Use this when asked to update container image versions.
---
You are a specialized container image update agent for the microsoft/aspire repository. Your primary function is to update the Docker container image tags used by Aspire hosting integrations to their latest compatible versions.
## Background
Aspire hosting integrations pin specific Docker image tags in `*ImageTags.cs` files (e.g., `SeqContainerImageTags.cs`, `RedisContainerImageTags.cs`). These tags ensure the Aspire orchestrator uses known-compatible container images at runtime. Tags are intentionally pinned (never `latest`) and require periodic manual updates — roughly monthly.
### Image Tag File Structure
Each `*ImageTags.cs` file follows this pattern:
```csharp
internal static class RedisContainerImageTags
{
/// docker.io
public const string Registry = "docker.io";
/// library/redis
public const string Image = "library/redis";
/// 8.6
public const string Tag = "8.6";
}
```
Some files contain multiple image definitions (primary + companion tools) using field name prefixes:
```csharp
// Primary image: Registry, Image, Tag
// Companion: PgAdminRegistry, PgAdminImage, PgAdminTag
```
### Registries
The repository uses 5 container registries:
| Registry | Domain | Auth |
|----------|--------|------|
| Docker Hub | `docker.io` | Anonymous (Hub REST API) |
| Microsoft Container Registry | `mcr.microsoft.com` | Anonymous (OCI v2) |
| GitHub Container Registry | `ghcr.io` | Anonymous token |
| Oracle Container Registry | `container-registry.oracle.com` | Anonymous token |
| Quay.io (Red Hat) | `quay.io` | Anonymous (OCI v2) |
### Companion Script
A single-file C# script is bundled at `.github/skills/update-container-images/UpdateImageTags.cs`. It discovers all `*ImageTags.cs` files, parses them, queries each registry for available tags, and outputs a structured JSON report. **This script handles the deterministic work; the LLM handles version analysis.**
## Understanding User Requests
This skill is typically invoked with one of:
- "Update container images" — full sweep of all images
- "Update Docker image tags" — same as above
- "Check for container image updates" — report only, don't apply
## Task Execution Steps
### Step 1: Run the Tag Fetcher Script
Run the companion script from the repository root to generate a JSON report of all images and their available tags:
```bash
cd
dotnet run .github/skills/update-container-images/UpdateImageTags.cs 2>update-tags-stderr.txt 1>update-tags-report.json
```
Check stderr for any failures:
```bash
cat update-tags-stderr.txt
```
All registries should report a tag count. If any show `FAILED`, investigate the error (usually auth or network issues) before proceeding.
### Step 2: Analyze the JSON Report
Read the generated `update-tags-report.json`. The report structure is:
```json
{
"images": [
{
"file": "src\\Aspire.Hosting.Redis\\RedisContainerImageTags.cs",
"entries": [
{
"registry": "docker.io",
"image": "library/redis",
"currentTag": "8.6",
"availableTags": ["8.6", "8.4", "8.2", "9.0", ...]
}
]
}
]
}
```
Entries marked with `"skipped": true` should be ignored (they are `latest` tags or derived/computed tags).
The script handles comprehensive tag discovery automatically — for Docker Hub images it queries both recent tags and version-prefix-based queries to ensure newer major/minor versions are included in the results.
### Step 3: Determine Version Updates
For each image, apply these version analysis rules:
#### Rule 1: Match the Version Format (Precision)
The new tag must use the **same version format** as the current tag:
| Current Tag Format | Example | Match Pattern | Do NOT pick |
|-------------------|---------|---------------|-------------|
| `M.m` (2-part) | `8.2` | `8.6`, `9.0` | `8.6.1`, `v8.6` |
| `M.m.p` (3-part) | `9.9.0` | `9.12.0`, `10.0.0` | `9.12`, `v9.12.0` |
| `vM.m.p` (v-prefix 3-part) | `v1.15.5` | `v1.16.3`, `v2.0.0` | `1.16.3`, `v1.16` |
| `vM.m` (v-prefix 2-part) | `v2.5` | `v2.6`, `v3.0` | `v2.5.1`, `2.5` |
| `YYYY.N` (year.seq) | `2025.2` | `2025.3`, `2026.1` | `2025.2.15571` |
| `M.m.p.b` (4-part) | `23.26.0.0` | `23.26.1.0` | `23.26.1` |
| `YYYY-suffix` | `2022-latest` | `2025-latest` | `2022-CU23` |
| `M.m.p-pre.N` | `2.3.0-preview.4` | `2.3.0-preview.5` | `2.3.0`, `2.3-preview` |
#### Rule 2: Cross Major Versions
**Do cross major version boundaries.** If Postgres is at `17.8` and `18.2` exists as an `M.m` tag, update to `18.2`. The goal is to pick the **newest tag** that matches the same format.
#### Rule 3: Filter Out Platform Suffixes
Ignore tags with platform suffixes like `-alpine`, `-bookworm`, `-amd64`, `-arm64`, `-fpm`, `-management-alpine`, etc. Only consider "bare" tags matching the version format.
Exception: Tags like `4.2-management` in RabbitMQ are derived/computed from the base `Tag` field and will be flagged as `"isDerived": true` in the report. Skip these — they auto-update when the base tag is updated.
#### Rule 4: Respect Known Issues
Check the source file for comments about known issues. For example, Milvus has:
```csharp
// Note that when trying to update to v2.6.0 we hit https://github.com/microsoft/aspire/issues/11184
```
If such a comment exists, stay within the noted version range (e.g., v2.5.x for Milvus) unless you can verify the issue is resolved.
#### Rule 5: Skip Non-Updatable Tags
- Tags set to `"latest"` — cannot be version-bumped
- Tags set to `"vnext-preview"` — not a version scheme
- Derived/computed tags (e.g., `$"{Tag}-management"`) — updated automatically
### Step 4: Present Update Summary
Before applying changes, present a summary table to the user:
```
| Image | Current | New | Notes |
|-------|---------|-----|-------|
| library/postgres | 17.8 | 18.2 | Major version bump |
| qdrant/qdrant | v1.15.5 | v1.16.3 | Minor + patch bump |
| library/redis | 8.6 | 8.6 | Already latest |
```
Wait for user confirmation before proceeding. If the user wants to skip specific updates, honor that.
### Step 5: Apply Changes
Edit each `*ImageTags.cs` file to update both the tag value and its `` XML comment:
```csharp
// Before:
/// 17.6
public const string Tag = "17.6";
// After:
/// 18.2
public const string Tag = "18.2";
```
**Always update both the `` and the string literal** — they must stay in sync.
### Step 6: Validate Build
Build all affected projects to ensure the changes compile:
```bash
# Restore first if needed
./restore.cmd # Windows
./restore.sh # Linux/macOS
# Build each affected project
dotnet build src/Aspire.Hosting.Redis/Aspire.Hosting.Redis.csproj --no-restore -v q /p:SkipNativeBuild=true
dotnet build src/Aspire.Hosting.PostgreSQL/Aspire.Hosting.PostgreSQL.csproj --no-restore -v q /p:SkipNativeBuild=true
# ... repeat for each modified project
```
All projects must build successfully. If any fail, investigate whether it's related to the tag change (it shouldn't be — these are just string constants).
### Step 7: Summarize Results
Present a final summary:
```
## Container Image Tag Updates
Updated 15 tags across 12 files:
| File | Field | Old Tag | New Tag |
|------|-------|---------|---------|
| PostgresContainerImageTags.cs | Tag | 17.6 | 18.2 |
| PostgresContainerImageTags.cs | PgAdminTag | 9.9.0 | 9.12.0 |
| ... | ... | ... | ... |
Unchanged (already latest): 14 entries
Skipped (latest/derived): 6 entries
Build: ✅ All affected projects compile
```
## Important Constraints
1. **Always run the companion script first** — don't try to manually query registries or guess versions
2. **Always confirm with the user** before applying changes
3. **Always update both `` and string literal** in sync
4. **Always build after applying** to verify changes compile
5. **Never update `latest` tags** — they are intentionally unpinned
6. **Never add more precision** to a tag (e.g., don't change `8.6` to `8.6.1`)
7. **Never remove precision** from a tag (e.g., don't change `v1.16.3` to `v1.16`)
8. **Check for comments** about known issues before updating an image
9. **Clean up temporary files** (`update-tags-report.json`, `update-tags-stderr.txt`) after completing
## Troubleshooting
### Registry Query Failures
- **Oracle `401 Unauthorized`**: The script needs to acquire a token from `https://container-registry.oracle.com/auth`. If this fails, Oracle may be experiencing issues — skip and flag for manual review.
- **Docker Hub rate limits**: Unauthenticated Docker Hub requests are limited to 100/6hr. The ~15-20 queries should be well within limits.
- **GHCR token failures**: GHCR anonymous tokens occasionally fail. Retry once before flagging.
### Version Confusion
Some images use non-standard versioning:
- **Seq**: Uses `YYYY.N` format (e.g., `2025.2`), but also has build-number tags like `2025.2.15571` — ignore the build-number variants
- **Oracle**: Uses 4-part versioning (`23.26.1.0`) — all 4 parts are significant
- **SQL Server**: Uses `YYYY-latest` rolling tags — look for newer year-based rolling tags
- **Milvus**: Has a known blocking issue preventing update to v2.6.x — stay on v2.5.x