Move service application and database settings into an embedded modal with a footer, subtitle helper, and Docker restart-count control. Accept max_restart_count on the service applications API, cap compose YAML collection aliases, and tighten status, backup, and database sidebar layouts.
S3-compatible and legacy buckets may include uppercase letters. Relax the bucket name pattern, validation messages, and tests so those names are accepted while still rejecting invalid characters and formats.
Normalize schemes when comparing routing identities and include per-service
Compose domains in conflict checks. Show the conflicting service name and
align the service domains heading layout.
Add indexing, redirect, and hostname regeneration controls to domain editors. Queue DNS checks only for scheme or hostname changes and show progress consistently across application, service, and preview domains.
Add restartLimitReached to the notification event toggle list so admins can disable restart-limit email notifications. Cover the toggle with an authorization test.
Pull image-based Docker Compose services with --ignore-buildable before stopping the current deployment so image-only services are ready first. Fail the deploy if pull fails, and run the pull on the runtime server when a build server is used.
Extract a shared view-toggle utility so list/grid switchers use the same 32px control height instead of mixed h-8/h-9 styles. Point section-heading View all links at the shared button class so they match other actions.
Honor an explicit sidebarCollapsed preference instead of always auto-collapsing with a second nav. Show the primary domain on the application access card, use a persisted GitLab name in the page title after delete, and shrink the compose view switcher.
Pass S3 endpoint and keys as helper container env vars and run mc alias
via those variables so the stored import command no longer contains
access keys or secrets. Start the helper in a separate remote process,
use the storage filesystem adapter for object checks, and remove a
credential temp file during import cleanup when the path is safe.
Move database import temp/container cleanup into a queued listener with retries, and clear the import running state on failed restore starts. Tighten Cloudflare replace/delete to the current server IP and owning resource, notify when DNS jobs fail, and pass domain-removal confirmation through remove-by-key. Document import request oneOf schemas without extra properties.
Single-database mysql/mariadb imports accepted .gz files but redirected
them straight into the client. Pipe through the existing gunzip fallback
used by dump-all restores.
Hold a cache lock keyed by database UUID across the active-import
check and remote_process so two concurrent requests cannot both
queue a restore against the same database.
ConfigureDnsRecordJob requires a string content value. Guard
configureDnsAfterDomainAdd with the same blank and FILTER_VALIDATE_IP
checks used by createManagedDnsRecord so a missing or hostname-only
server address cannot TypeError after the domain is saved.
PostgreSQL single-database restores no longer always replace existing
objects. Update the file and S3 confirmation dialogs so they match the
callout instead of claiming all data will be replaced.
Allow single-database PostgreSQL imports to drop matching objects before restore. The API and import form accept replace_existing, which adds --clean --if-exists to pg_restore, and pg_restore now uses --exit-on-error.
Remove the enable/disable toggle from the server UI, logs page, and
Sentinel API so is_sentinel_enabled is derived and read-only. Enable
existing regular servers via migration, start Sentinel after validate-
and-install, and drop the daily ServerManagerJob restart.
Replace the collapsible mobile configuration menu with an always-visible
responsive grid, stack the mobile heading, and swap the domain editor
unsaved bar for a sticky Save footer with a scrollable form body.
Centralize scp-style Git URL parsing so user@host:path (including custom
usernames and embedded ports) is accepted and converted to HTTPS for
public clones, API create, webhooks, validation, and commit/branch links.
Use resolveStoredTeam() instead of teams()->first() when picking the
next active team after a team deletion or when an admin switches into
a user's account, so a valid stored preference wins over an arbitrary
first team. DeleteTeam now returns null when the deletion leaves the
owner with multiple teams, deferring to the selection screen instead
of guessing. refreshSession also stops persisting current_team_id
while impersonating, so viewing another user's account no longer
overwrites their real last-active team.
Make clearStoredTeamIfMatches perform an atomic conditional UPDATE
so a concurrent team switch isn't clobbered, and call it for the
deleting owner in DeleteTeam so their stored team id doesn't point
at a deleted team. refreshSession now falls back to
resolveStoredTeam() instead of an arbitrary first team. Add a
return type to SelectTeam::render() and tests covering owner
deletion and concurrent-selection preservation.