removeJob() for type 'schedule' computed the BullMQ repeatable key
using only the cron pattern, without the timezone. scheduleJob()
always registers the repeatable job with tz set (job.timezone || UTC),
so BullMQ's repeat key (name:jobId:endDate:tz:pattern) never matched
on removal — removeRepeatable silently no-op'd and the job kept
firing forever, surviving disable, delete, and reboots.
Forward timezone through removeJob (queue.ts), the /update-backup
lookup (index.ts), and the disable/delete call sites in the
schedule router.
Fixes#5233. Updating an enabled schedule (cron/timezone change) called
schedule() (create-backup), which only adds a new repeatable job and
never removes the one registered under the old cron pattern, leaving
an orphaned BullMQ job running in Redis indefinitely. Switched to
updateJob() (update-backup), which looks up the currently registered
repeatable pattern, removes it, then re-adds — same pattern already
used in backup.ts for backups.
--project-directory was added unconditionally in 87b914996 to fix
relative bind mount resolution for git-based compose deploys with a
nested composePath (#5181). It also moves where build.context and the
generated .env resolve, breaking any compose file in a subdirectory
that has context: . alongside its Dockerfile (#5230), or interpolates
env vars (#5242).
Only pin --project-directory when the compose actually has mounts
configured; otherwise build.context and .env resolve against the
compose file's own directory like plain docker compose. Also pass
--env-file explicitly pointing at the generated .env next to the
compose file, so it's found even when --project-directory is pinned
(credit: tonnenpinguin, PR #5235).
Fixes#5230Fixes#5242
The loading branch added in the previous commit split the fallback cell
two ways: spinner while in flight, "No results." otherwise. That second
branch also catches the failure case -- when readStatsLogs errors,
statsLogs stays undefined and isLoading goes false, so a failed request
renders as a successful empty response.
The query's isError/error were not destructured. The cell now branches
three ways and reports the error through AlertBlock, matching how
ShowTraefikSystem surfaces a failed readDirectories query.
Trims and lowercases the CLI email arg to match the normalization
used elsewhere for user emails, so a differently-cased or
whitespace-padded email no longer falsely reports "User not found".
Previously reset-password only reset the owner account's password.
Passing an email as an argument now resets that specific user's
password instead, while omitting it keeps the existing owner-reset
behavior.
Also scopes the update to the credential (password-based) account
row via providerId, and fixes the success check to verify a row was
actually updated instead of always reporting success.
validateDomain accepted an arbitrary serverId and used it to look up
the server and SSH into it, without checking it belonged to the
caller's active organization. A member with domain:read could probe
other orgs' servers and get their SSH/detected IPs back in the
response.
Addresses greptile review on #5214
Free monitoring resolved containers via a local Dockerode listContainers()
call, which only sees containers scheduled on the host running Dokploy.
For Stack services with tasks on other Swarm nodes, this always fell into
the "Container not running" branch even when the task was healthy
elsewhere, since local docker.listContainers() can't see remote-node
containers.
Now falls back to `docker service ps` (Swarm-aggregated, works from any
manager regardless of task placement) to tell a genuinely stopped
container apart from one running on another node, and reports that
distinction in the WS close reason instead of the misleading message.
Also fixes a client-side race where the stats websocket connected with
an empty appName on first mount (before the container selector settled),
and surfaces the close reason as a toast instead of only logging it.
Closes#5134. The container list queries in the Logs tab only fetched
once on mount, so if the underlying container was replaced (restart,
reschedule, redeploy) while the page stayed open, the native log
WebSocket kept streaming from the stale id and either froze or failed
with 'No such container'.
Poll the container list every 5s and reconcile the selected id against
it, matching the pattern already used for automatic selection.
Attaching Basic Auth or a Redirect to an application with no domains
wrote an empty routers/services block to the app's dynamic config
file. Traefik's file provider rejects that as invalid and aborts its
watcher, blocking config updates for every other application.
Fixes#5189
Compose deployments using a git provider (GitHub/GitLab/Gitea/Bitbucket/
custom Git) clone the repo verbatim, so composePath can point into a
subdirectory (e.g. deploy/docker-compose.yml). Docker Compose resolves
relative bind mounts (../files) against the compose file's own directory,
not against the code/ dir where file mounts are actually written, so the
mount silently binds an empty directory instead of the configured content.
Pin --project-directory to code/ when building the docker compose command
so relative mounts always resolve the same way raw deployments already do.
Fixes#5181
Network chart showed values labeled as KB/s, but the underlying data
is MB totals accumulated since boot. Convert to GB and relabel as
Network Total (since Boot).
Fixes#5115
Network sync compared only by name, so a network deleted and
recreated with the same name but different attributes (driver,
attachable, etc.) was reported as already in sync.
Store Docker's network Id (dockerId column) and compare it against
the live Id during sync. A name match with a dockerId mismatch is
now surfaced as a new "changed" state, with an Update action to
refresh the DB row from the live network.
Fixes#5179
Escaping in prepareEnvironmentVariablesForFile escaped every $,
including inside a deliberate ${VAR} reference, breaking Compose's
documented .env variable interpolation. Only escape $ that isn't
part of a ${IDENTIFIER} sequence.
Fixes#5151