This release introduces a dedicated migrations image to replace the previous extract-from-app-image approach, which never worked due to the app image being distroless. The chart now consumes ghcr.io/lerianstudio/plugin-br-pix-jd-migrations, a purpose-built image containing golang-migrate with the application’s SQL schema baked in.
| Setting | v0.2.0 | v0.3.0 |
|---|---|---|
| Chart Version | 0.2.0 | 0.3.0 |
| App Version | 1.12.0-beta.8 | 1.13.0-beta.4 |
| Migrations Image | Extracted from app image | Dedicated plugin-br-pix-jd-migrations image |
| Migrations Job Architecture | initContainer + emptyDir + generic migrate/migrate | Single container with baked-in schema |
What changed:
The migrations Job previously attempted to extract /migrations from the app image using an initContainer running /bin/sh -c cp, then applied the schema from a generic migrate/migrate:v4.18.1 container over a shared emptyDir. This approach never worked because the app image is gcr.io/distroless/static-debian12, which ships no shell and no cp binary. The initContainer died with:
exec: "/bin/sh": stat /bin/sh: no such file or directory
The bug remained invisible because the only two production installs were multi-tenant, where the migrations Job is skipped entirely (the Tenant Manager owns per-tenant schema migrations).
Why it matters:
The first single-tenant install hit ImagePullBackOff immediately. The chart now consumes a dedicated migrations image (ghcr.io/lerianstudio/plugin-br-pix-jd-migrations) that contains golang-migrate with the SQL schema baked in, matching the pattern used by all sibling charts (br-ccs, br-sisbajud, br-sfn, matcher, plugin-access-manager, plugin-br-bank-transfer, streaming-hub).
Migration impact:
| Component | v0.2.0 | v0.3.0 |
|---|---|---|
| Migrations source | App image (/migrations extracted via shell) |
Dedicated migrations image |
| initContainers | wait-for-postgres + extract-migrations |
wait-for-postgres only (bundled DB) |
| Volumes | emptyDir for extracted schema |
None |
| Image configuration | migrations.migrateImage |
migrations.image.repository + migrations.image.tag |
| Password handling | In-shell DSN assembly | ENTRYPOINT with percent-encoding |
Before (v0.2.0):
migrations:
enabled: true
migrateImage: migrate/migrate:v4.18.1
sourcePath: /migrations
table: ""
After (v0.3.0):
migrations:
enabled: true
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "" # MUST BE PINNED — see warning below
pullPolicy: IfNotPresent
table: ""
Warning: The
migrations.image.tagfield must be explicitly pinned. The fallback to.Chart.AppVersionis a convention-compliant default, not a correct one: the release pipeline builds only the components a commit touched, soapi,worker, andmigrationstags legitimately diverge. No single tag names all three. As measured on 2026-08-27,apiandmigrationsexist at1.13.0-beta.4, butworkerstops at1.13.0-beta.3. An unpinned tag surfaces asImagePullBackOffon a PreSync hook, which blocks the entire sync.
Important: The chart will fail the render with a descriptive error if
migrations.image.repositoryis empty:ERROR: migrations.image.repository is empty. An empty repository renders as ":<tag>", which the API server rejects with InvalidImageName — and this Job is a PreSync hook, so it blocks the whole sync. Set migrations.image.repository (default: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations).
Removed fields:
| Field | v0.2.0 | v0.3.0 | Replacement |
|---|---|---|---|
migrations.migrateImage |
migrate/migrate:v4.18.1 |
Removed | migrations.image.repository + migrations.image.tag |
migrations.sourcePath |
/migrations |
Removed | Baked into migrations image |
Template changes:
The migrations Job template (templates/common/migrations-job.yaml) was rewritten to:
extract-migrations initContainer (no shell, no emptyDir)emptyDir volume and its mountmigrate/migrate container with the dedicated migrations imagecommand: [/bin/sh, -c, migrate -database "postgres://..."])POSTGRES_MIGRATIONS_TABLE environment variable (replaces URL query parameter)wait-for-postgres initContainer inside an `` guard (external databases don’t need it)Before (v0.2.0):
initContainers:
- name: wait-for-postgres
# ...
- name: extract-migrations
image:
command:
- /bin/sh
- -c
- cp -R /migrations/. /workdir/
volumeMounts:
- name: migrations
mountPath: /workdir
containers:
- name: migrations
image: migrate/migrate:v4.18.1
command:
- /bin/sh
- -c
- >-
migrate -path /migrations
-database "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@..."
up
volumeMounts:
- name: migrations
mountPath: /migrations
readOnly: true
volumes:
- name: migrations
emptyDir: {}
After (v0.3.0):
initContainers:
- name: wait-for-postgres
# ...
containers:
- name: migrations
image:
# NO command: — ENTRYPOINT handles DSN assembly and percent-encoding
env:
- name: POSTGRES_HOST
value: ...
- name: POSTGRES_USER
value: ...
- name: POSTGRES_NAME
value: ...
- name: POSTGRES_SSLMODE
value: ...
- name: POSTGRES_MIGRATIONS_TABLE
value:
# NO volumeMounts
# NO volumes
Operational impact:
wait-for-postgres for bundled PostgreSQL)@ : / ? # & + % or spaces are now supported via the image’s ENTRYPOINT percent-encoding (the old in-shell DSN could not handle them)migrations.table override is now passed as POSTGRES_MIGRATIONS_TABLE environment variable instead of a URL query parameterImagePullBackOff if migrations.image.tag is unpinned and the tag doesn’t exist, rather than silently extracting an empty schemaThe chart now consumes a purpose-built migrations image that contains golang-migrate with the application’s SQL schema baked in. This matches the architecture of all sibling charts and eliminates the shell/emptyDir workaround that never functioned.
Key characteristics:
ghcr.io/lerianstudio/plugin-br-pix-jd-migrationsmigrate/migrate with migrations/ directory baked inPOSTGRES_* environment variables and applies schema@ : / ? # & + % and spaces)Configuration:
migrations:
enabled: true
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "1.13.0-beta.4" # MUST BE PINNED
pullPolicy: IfNotPresent
table: "" # Optional x-migrations-table override
Environment variables:
The migrations container receives:
| Variable | Source | Purpose |
|---|---|---|
POSTGRES_HOST |
postgresql.host or bundled subchart |
Database hostname |
POSTGRES_USER |
global.datastores.postgres.user or chart default |
Database user |
POSTGRES_NAME |
api.configmap.POSTGRES_NAME or chart default |
Database name |
POSTGRES_SSLMODE |
global.datastores.postgres.ssl or chart default |
SSL mode |
POSTGRES_PASSWORD |
api.secrets.POSTGRES_PASSWORD or bundled subchart |
Database password (via secretKeyRef) |
POSTGRES_MIGRATIONS_TABLE |
migrations.table |
Optional migrations table name override |
Note: The
POSTGRES_MIGRATIONS_TABLEvariable is only set ifmigrations.tableis non-empty. When omitted, golang-migrate uses its default table name (schema_migrations).
Password security:
The image’s ENTRYPOINT percent-encodes the password before assembling the DSN, which means passwords can now contain characters that would break URL parsing:
@ (at sign): (colon)/ (slash)? (question mark)# (hash)& (ampersand)+ (plus)% (percent)The old in-shell DSN assembly (postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@...) could not handle these characters and would fail with connection errors or parse the DSN incorrectly.
The chart’s appVersion has been updated from 1.12.0-beta.8 to 1.13.0-beta.4, reflecting the release train the chart was cut against.
| Component | v0.2.0 | v0.3.0 |
|---|---|---|
Chart appVersion |
1.12.0-beta.8 | 1.13.0-beta.4 |
Important: The
appVersionis a fallback default forapi.image.tag,worker.image.tag, andmigrations.image.tag. Production values should always pin tags explicitly rather than inheriting fromappVersion, because the release pipeline builds only the components a commit touched. Tags legitimately diverge across components.
Recommended configuration:
api:
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd
tag: "1.13.0-beta.4" # Explicit pin
worker:
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-worker
tag: "1.13.0-beta.3" # May differ from api
migrations:
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "1.13.0-beta.4" # May differ from worker
New structure (v0.3.0):
migrations:
# -- Enable the migrations Job (incompatible with multi-tenant mode)
enabled: true
# -- Kubernetes Job retry configuration
backoffLimit: 3
activeDeadlineSeconds: 600
ttlSecondsAfterFinished: 600
# -- Timeout for the wait-for-postgres initContainer (bundled DB only)
waitTimeoutSeconds: 300
# -- Annotations added to the Job
annotations: {}
# -- Resource requests/limits for the migrations container
resources: {}
# -- Image used to wait for the datastore to accept connections (bundled DB only)
waitImage: busybox:1.37
# -- Dedicated migrations image: golang-migrate with this app's SQL baked in
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
# PIN THIS. The fallback to .Chart.AppVersion is a convention-compliant default,
# not a correct one: the release pipeline builds only the components a commit
# touched, so api, worker and migrations tags legitimately DIVERGE and no single
# tag names all three.
tag: ""
pullPolicy: IfNotPresent
# -- Optional x-migrations-table override, passed to the image as
# POSTGRES_MIGRATIONS_TABLE; empty uses golang-migrate's default (schema_migrations)
table: ""
Field changes:
| Field | v0.2.0 | v0.3.0 | Notes |
|---|---|---|---|
migrations.migrateImage |
migrate/migrate:v4.18.1 |
Removed | Replaced by migrations.image.repository |
migrations.sourcePath |
/migrations |
Removed | Baked into migrations image |
migrations.image |
N/A | New | Structured image configuration |
migrations.image.repository |
N/A | New | Default: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations |
migrations.image.tag |
N/A | New | Must be pinned — see warning above |
migrations.image.pullPolicy |
N/A | New | Default: IfNotPresent |
migrations.table |
"" |
"" |
Now passed as POSTGRES_MIGRATIONS_TABLE env var |
Update your values.yaml to explicitly set the migrations image tag. Do not rely on the appVersion fallback.
Add to your values:
migrations:
enabled: true
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "1.13.0-beta.4"
pullPolicy: IfNotPresent
Warning: If you leave
migrations.image.tagempty, the chart will fall back to.Chart.AppVersion(1.13.0-beta.4). This may work for this release, but tags diverge across components in future releases. An unpinned tag that doesn’t exist will causeImagePullBackOffon a PreSync hook, blocking the entire Helm sync.
Confirm the migrations image exists at your pinned tag before upgrading:
docker pull ghcr.io/lerianstudio/plugin-br-pix-jd-migrations:1.13.0-beta.4
If the image doesn’t exist, check the releases page for available tags.
If you previously customized migrations.migrateImage or migrations.sourcePath, remove those fields and migrate to the new migrations.image structure.
Before (v0.2.0):
migrations:
enabled: true
migrateImage: migrate/migrate:v4.18.1
sourcePath: /migrations
table: custom_migrations
After (v0.3.0):
migrations:
enabled: true
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "1.13.0-beta.4"
pullPolicy: IfNotPresent
table: custom_migrations
Note: The
migrations.tablefield is unchanged in behavior — it still overrides the migrations table name — but is now passed to the image as thePOSTGRES_MIGRATIONS_TABLEenvironment variable instead of a URL query parameter.
Optional: Customize migrations image registry
If you mirror images to a private registry:
global:
imageRegistry: registry.example.com
migrations:
image:
repository: ghcr.io/lerianstudio/plugin-br-pix-jd-migrations
tag: "1.13.0-beta.4"
The chart will prepend global.imageRegistry to the repository, rendering:
registry.example.com/ghcr.io/lerianstudio/plugin-br-pix-jd-migrations:1.13.0-beta.4
helm diff upgrade plugin-br-pix-jd oci://registry-1.docker.io/lerianstudio/plugin-br-pix-jd-helm --version 0.3.0 -n plugin-br-pix-jd
Note: Requires the helm-diff plugin. Install with:
helm plugin install https://github.com/databus23/helm-diff
helm upgrade plugin-br-pix-jd oci://registry-1.docker.io/lerianstudio/plugin-br-pix-jd-helm --version 0.3.0 -n plugin-br-pix-jd