rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

rfc(decision): Distroless base images - #157

Open
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images
Open

rfc(decision): Distroless base images#157
oioki wants to merge 7 commits into
mainfrom
rfc/distroless-base-images

Conversation

@oioki

@oiokioioki commented Mar 27, 2026

Copy link
Copy Markdown
Member

@oioki
oiokiforce-pushed the rfc/distroless-base-images branch from 21cb505 to 75cc51eCompareMarch 27, 2026 09:39
@oioki
oioki marked this pull request as ready for review March 27, 2026 15:30

# Resolved questions

- **Long-term commitment to DHI:** Despite Docker Inc having a history of unexpected licensing and policy changes (Hub rate limiting, Desktop licensing, etc.), DHI was recently made public under Apache 2.0, and a rollback of that decision seems unlikely. If needed, Google Distroless is a practical drop-in fallback — it lags a few patch versions behind but is otherwise compatible. Other solutions may also emerge over time. We can go with DHI images as a default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we would be able to maintain the images if we had to?

@oiokioiokiMay 27, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we really don't want to do this (as it's not Sentry business) but I tried doing this literally from scratch in a couple of hours, and drop-in replacement of the base image worked fine to the point of passing all smoke tests on snuba:

So, if we had to, it is doable nowadays.

Comment threadtext/0157-distroless-base-images.md Outdated
- **Snuba and getsentry:** These are the largest remaining Python services. The Snuba PoC (https://github.com/getsentry/snuba/pull/7753, https://github.com/getsentry/snuba/pull/7821, https://github.com/getsentry/snuba/pull/7829, https://github.com/getsentry/ops/pull/19824) showed it is feasible. What is the sequencing and who owns driving this to completion?
- **Local development compatibility:** Are there any blockers that might disrupt local development workflows when switching to distroless? So far this appears to be a non-issue — for example, Snuba distroless containers work fine in `sentry devservices` (https://github.com/getsentry/snuba/pull/7829).
- **Services with non-trivial runtime deps:** Some services (e.g. uptime-checker with OpenSSL for certificate validation, or services using external libraries) may need extra work. Are there any blockers that make distroless infeasible for them?
- **Public mirrors for anonymous access:** Pulling directly from `dhi.io` requires a Docker login, which complicates CI pipelines and local image builds for contributors. Should we commit to maintaining public mirrors at `ghcr.io/getsentry/dhi` to allow unauthenticated pulls? See current PoC: https://github.com/getsentry/dhi.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needing to login could be disruptive to self-hosted users.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are going to mirror the limited set of images (for start, python and node) on artifact registries controlled by us: either GHCR (could be more native for self-hosted) or Public Artifact Registry in GCP (could be faster for SaaS builds?). In both cases, pulling base images from those won't require authentication.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: this is now implemented and the login requirement is gone for downstream consumers. We run a public pull-through mirror on GCP Artifact Registry at us-docker.pkg.dev/sentryio/dhi-mirror, serving python and node with no authentication. It transparently caches from dhi.io on demand — no hand-maintained mirroring pipeline — so self-hosted users and CI pull base images without logging in. Infra PR: getsentry/ops#21183. I've moved this to the RFC's Resolved questions section.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like us to put in some effort ahead of time to validate that the debugging flow is very smooth - we've definitely run into various issues attempting to attach debugger pods in some of the places we've already swapped out more minimal images.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry-kube debug is a full replacement of the kubectl exec workflow. I'd even consider it a better experience as it allows elevating permissions to allow attaching runtime profilers, which isn't possible with just kubectl exec.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

@Dav1ddeDav1ddeMay 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the only downside for debugging is docker-compose in dev setups and self-hosted where sentry-kube debug doesn't work.

@untitakeruntitaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sentry uses /dev/shm for multiprocess IPC, and /tmp for random stuff related to release artifacts and other file uploads.

some of these images explicitly say that /tmp is "hardened" (so basically unusuable). while we generally mount tmpfs on /tmp into the container (making this a non-issue), i'm not sure that we do it consistently in all pods that require it. also no idea if self-hosted has this kind of setup at all.

it may be desirable to enforce in the ops repo that tmpfs is mounted in absolutely every container in /dev/shm and /tmp, rather than relying on smoke tests. we have had incidents where new deployments were missing those mounts leading to really weird issues in multiprocess consumers, so some systematic enforcement would be nice to have for other reasons.

Comment on lines +140 to +144
Distroless containers have no shell. You cannot `exec` into a running container and run arbitrary commands. Debugging requires:

- Attaching an ephemeral debug container with a shell to the running pod (e.g. [`sentry-kube debug`](https://github.com/getsentry/sentry-infra-tools/blob/main/sentry_kube/cli/debug.py))
- Using application-level tooling (e.g. interactive shells provided by the framework) rather than OS-level tools, e.g. `getsentry shell`
- Investing in proper observability (logs, metrics, tracing) instead of ad-hoc inspection

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, our existing application images are already very slim, so exec is not very useful. debug is a better experience today, and anytime I use exec, i immediately go into getsentry shell.

Move the "public mirrors for anonymous access" item to Resolved questions:
the login concern (disruptive to self-hosted users and CI) is addressed by
the public GCP Artifact Registry pull-through mirror at
us-docker.pkg.dev/sentryio/dhi-mirror, which serves python/node without
authentication and needs no hand-maintained mirroring pipeline.
Refs getsentry/ops#21183
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@Dav1ddeDav1dde left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

How do we deal with this?

Possibly out of scope for this RFC.

@oioki

Copy link
Copy Markdown
MemberAuthor

Self-Hosted needs curl and potentially other tools available for docker-compose health checks, since docker compose does not have any way of natively using HTTP for health checks.

For snuba, we re-implemented health checks in python: getsentry/self-hosted#4352

@Dav1dde

Dav1dde commented Jun 17, 2026

Copy link
Copy Markdown
Member

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

@oioki

Copy link
Copy Markdown
MemberAuthor

@oioki that doesn't work for distroless containers which do not have a Python interpreter available, which is very similar to the curl/bash issue (just now with Python).

We can still add curl binary if we really need to, not a big deal.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@oioki@Dav1dde@markstory@mwarkentin@phacops@untitaker@ghengiskhanh