Compare commits

..

13 Commits

Author SHA1 Message Date
dosubot[bot]
0c5d49ca66 docs: Dosu updates for PR #3894 2026-03-05 06:48:56 +00:00
Mauricio Siu
0195119a86 Merge pull request #3894 from Dokploy/3888-deploy-error-client-version-153-is-too-new-on-synology-920
feat: enhance Docker configuration with environment variables for API…
2026-03-05 00:47:46 -06:00
Mauricio Siu
48a577e792 feat: enhance Docker configuration with environment variables for API version, host, and port 2026-03-05 00:46:13 -06:00
Mauricio Siu
bf7a75dd9f Merge pull request #3882 from aak-lear/fix/rollback-registry-auth
fix: add docker login before rollback and fix execAsyncRemote argument order
2026-03-04 22:11:04 -06:00
Mauricio Siu
d316aa4401 Merge pull request #3893 from Dokploy/3853-web-server-backup-fails-when-unreadable-files-unix-sockets-named-pipes-exist-under-etcdokploy
fix: update rsync command in web-server backup to exclude special fil…
2026-03-04 21:37:15 -06:00
Mauricio Siu
f1b2cc35b3 fix: update rsync command in web-server backup to exclude special files and devices 2026-03-04 21:21:46 -06:00
lear
d2fabc998d refactor: reuse safeDockerLoginCommand from registry.ts instead of duplicating shEscape 2026-03-04 12:45:57 +03:00
lear
7185047eb7 fix: add docker login before rollback and fix execAsyncRemote argument order 2026-03-04 11:07:42 +03:00
Mauricio Siu
7121fbe50a Merge pull request #3881 from Dokploy/3864-file-mount-content-not-updated-on-host-when-edited-in-advanced-tab-ui-wordpress-service
refactor: simplify createMount mutation by returning the promise dire…
2026-03-03 22:59:32 -06:00
Mauricio Siu
36cf3a69fc refactor: simplify createMount mutation by returning the promise directly
Updated the createMount mutation to return the promise from createMount directly, enhancing readability. Additionally, adjusted the serviceType schema definition for clarity by removing the default value assignment.
2026-03-03 22:55:46 -06:00
Mauricio Siu
c34a01a173 Merge pull request #3880 from Dokploy/3876-auth-session-ui-not-updating-after-profile-picture-change
refactor: replace authClient with api.organization.active for active …
2026-03-03 22:39:04 -06:00
Mauricio Siu
9ac147a140 refactor: replace authClient with api.organization.active for active organization queries
Updated components to use the new API method for fetching the active organization, improving consistency across the codebase. This change enhances maintainability and aligns with recent API updates.
2026-03-03 22:37:42 -06:00
Mauricio Siu
20f79ac655 fix: update import statements to include file extensions for consistency 2026-03-03 15:35:37 -06:00
15 changed files with 99 additions and 34 deletions

View File

@@ -62,6 +62,16 @@ pnpm install
cp apps/dokploy/.env.example apps/dokploy/.env
```
### Optional Docker Configuration
The following environment variables can be added to your `.env` file if you need custom Docker daemon configuration:
- **DOCKER_API_VERSION**: Specify which Docker API version to use (optional)
- **DOCKER_HOST**: Specify a custom Docker daemon host (optional)
- **DOCKER_PORT**: Specify a custom Docker daemon port (optional)
These variables are typically not needed for standard local development but can be useful if you need to connect to a remote Docker daemon or require a specific Docker API version.
## Requirements
- [Docker](/GUIDES.md#docker)
@@ -171,6 +181,11 @@ curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.39.1/pack-v0.
- **Issue Association:** For any significant change, it's highly recommended to open an issue first to discuss the proposed solution with the community and maintainers. This ensures alignment and avoids duplicated effort. If your PR resolves an existing issue, please link it in the description (e.g., `Fixes #123`, `Closes #456`).
- **Large Features:** Pull Requests that introduce very large or broad features **will not be accepted** unless the idea is first outlined and discussed in a GitHub issue. Large features should be designed together with the Dokploy team so the project stays coherent and moves in the same direction. Open an issue to propose and align on the design before implementing.
### Pull Request Guidelines
- **Keep PRs small and focused.** Avoid very large PRs; prefer several smaller PRs (e.g., one template or one logical change per PR). This speeds up review and keeps the history clear.
- **Test before submitting.** Any PR that has not been tested by the contributor will be closed. This keeps the PR queue tidy and ensures that only contributions that have been verified by their authors are considered.
Thank you for your contribution!
## Templates

View File

@@ -32,6 +32,18 @@ Optional configuration for filtering and pagination:
- **LEMON_SQUEEZY_API_KEY** - API key for Lemon Squeezy integration
- **LEMON_SQUEEZY_STORE_ID** - Store ID for Lemon Squeezy integration
### Docker Configuration
Optional configuration for customizing Docker daemon connections:
- **DOCKER_API_VERSION** (optional) - Specifies which Docker API version to use when connecting to the Docker daemon. If not set, the Docker client uses the default API version.
- **DOCKER_HOST** (optional) - Specifies the Docker daemon host to connect to. If not set, uses the default Docker socket connection.
- **DOCKER_PORT** (optional) - Specifies the port for connecting to the Docker daemon. If not set, uses the default port.
These variables allow advanced users to customize how the Dokploy API server connects to Docker, which can be useful for connecting to remote Docker daemons or using specific API versions.
## API Endpoints
### GET /jobs

View File

@@ -1,4 +1,4 @@
import { logger } from "./logger";
import { logger } from "./logger.js";
const baseUrl = process.env.INNGEST_BASE_URL ?? "";
const signingKey = process.env.INNGEST_SIGNING_KEY ?? "";

View File

@@ -9,7 +9,7 @@ import {
updateCompose,
updatePreviewDeployment,
} from "@dokploy/server";
import type { DeployJob } from "./schema";
import type { DeployJob } from "./schema.js";
export const deploy = async (job: DeployJob) => {
try {

View File

@@ -24,7 +24,6 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
import { api } from "@/utils/api";
const organizationSchema = z.object({
@@ -55,8 +54,6 @@ export function AddOrganization({ organizationId }: Props) {
const { mutateAsync, isPending } = organizationId
? api.organization.update.useMutation()
: api.organization.create.useMutation();
const { refetch: refetchActiveOrganization } =
authClient.useActiveOrganization();
const form = useForm<OrganizationFormValues>({
resolver: zodResolver(organizationSchema),
@@ -89,7 +86,7 @@ export function AddOrganization({ organizationId }: Props) {
utils.organization.all.invalidate();
if (organizationId) {
utils.organization.one.invalidate({ organizationId });
refetchActiveOrganization();
utils.organization.active.invalidate();
}
setOpen(false);
})

View File

@@ -17,7 +17,8 @@ import { api } from "@/utils/api";
export const AddGithubProvider = () => {
const [isOpen, setIsOpen] = useState(false);
const { data: activeOrganization } = authClient.useActiveOrganization();
const { data: activeOrganization } = api.organization.active.useQuery();
const { data: session } = authClient.useSession();
const { data } = api.user.get.useQuery();
const [manifest, setManifest] = useState("");
@@ -52,7 +53,7 @@ export const AddGithubProvider = () => {
);
setManifest(manifest);
}, [data?.id, activeOrganization?.id, session?.user?.id]);
}, [activeOrganization?.id, session?.user?.id]);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
@@ -131,11 +132,7 @@ export const AddGithubProvider = () => {
Unsure if you already have an app?
</a>
<Button
disabled={
(isOrganization && organizationName.length < 1) ||
!activeOrganization?.id ||
!session?.user?.id
}
disabled={isOrganization && organizationName.length < 1}
type="submit"
className="self-end"
>

View File

@@ -55,7 +55,7 @@ export const AddInvitation = () => {
api.notification.getEmailProviders.useQuery();
const { mutateAsync: sendInvitation } = api.user.sendInvitation.useMutation();
const [error, setError] = useState<string | null>(null);
const { data: activeOrganization } = authClient.useActiveOrganization();
const { data: activeOrganization } = api.organization.active.useQuery();
const form = useForm<AddInvitation>({
defaultValues: {

View File

@@ -557,8 +557,7 @@ function SidebarLogo() {
const { mutateAsync: setDefaultOrganization, isPending: isSettingDefault } =
api.organization.setDefault.useMutation();
const { isMobile } = useSidebar();
const { data: activeOrganization } = authClient.useActiveOrganization();
const _utils = api.useUtils();
const { data: activeOrganization } = api.organization.active.useQuery();
const { data: invitations, refetch: refetchInvitations } =
api.user.getInvitations.useQuery();

View File

@@ -69,8 +69,7 @@ export const mountRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateMount)
.mutation(async ({ input }) => {
await createMount(input);
return true;
return await createMount(input);
}),
remove: protectedProcedure
.input(apiRemoveMount)

View File

@@ -355,4 +355,13 @@ export const organizationRouter = createTRPCRouter({
return { success: true };
}),
active: protectedProcedure.query(async ({ ctx }) => {
if (!ctx.session.activeOrganizationId) {
return null;
}
return await db.query.organization.findFirst({
where: eq(organization.id, ctx.session.activeOrganizationId),
});
}),
});

View File

@@ -2,8 +2,22 @@ import path from "node:path";
import Docker from "dockerode";
export const IS_CLOUD = process.env.IS_CLOUD === "true";
export const DOCKER_API_VERSION = process.env.DOCKER_API_VERSION;
export const DOCKER_HOST = process.env.DOCKER_HOST;
export const DOCKER_PORT = process.env.DOCKER_PORT;
export const CLEANUP_CRON_JOB = "50 23 * * *";
export const docker = new Docker();
export const docker = new Docker({
...(DOCKER_API_VERSION && {
version: DOCKER_API_VERSION,
}),
...(DOCKER_HOST && {
host: DOCKER_HOST,
}),
...(DOCKER_PORT && {
port: DOCKER_PORT,
}),
});
// When not set, use the legacy default so 2FA remains working for users who
// enabled it before BETTER_AUTH_SECRET was introduced .

View File

@@ -99,17 +99,15 @@ const createSchema = createInsertSchema(mounts, {
mountPath: z.string().min(1),
mountId: z.string().optional(),
filePath: z.string().optional(),
serviceType: z
.enum([
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis",
"compose",
])
.default("application"),
serviceType: z.enum([
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis",
"compose",
]),
});
export type ServiceType = NonNullable<

View File

@@ -16,7 +16,7 @@ function shEscape(s: string | undefined): string {
return `'${s.replace(/'/g, `'\\''`)}'`;
}
function safeDockerLoginCommand(
export function safeDockerLoginCommand(
registry: string | undefined,
user: string | undefined,
pass: string | undefined,

View File

@@ -23,7 +23,7 @@ import { findDeploymentById } from "./deployment";
import type { Mount } from "./mount";
import type { Port } from "./port";
import type { Project } from "./project";
import type { Registry } from "./registry";
import { type Registry, safeDockerLoginCommand } from "./registry";
export const createRollback = async (
input: z.infer<typeof createRollbackSchema>,
@@ -111,7 +111,7 @@ const deleteRollbackImage = async (image: string, serverId?: string | null) => {
const command = `docker image rm ${image} --force`;
if (serverId) {
await execAsyncRemote(command, serverId);
await execAsyncRemote(serverId, command);
} else {
await execAsync(command);
}
@@ -171,6 +171,23 @@ export const rollback = async (rollbackId: string) => {
);
};
const dockerLoginForRegistry = async (
registry: Registry,
serverId?: string | null,
) => {
const loginCommand = safeDockerLoginCommand(
registry.registryUrl,
registry.username,
registry.password,
);
if (serverId) {
await execAsyncRemote(serverId, loginCommand);
} else {
await execAsync(loginCommand);
}
};
const rollbackApplication = async (
appName: string,
image: string,
@@ -188,6 +205,14 @@ const rollbackApplication = async (
throw new Error("Full context is required for rollback");
}
// Ensure Docker daemon is authenticated with the rollback registry
// before updating the swarm service. The authconfig in CreateServiceOptions
// alone is not sufficient — Docker Swarm also relies on the daemon's
// cached credentials (~/.docker/config.json) to distribute auth to nodes.
if (fullContext.rollbackRegistry) {
await dockerLoginForRegistry(fullContext.rollbackRegistry, serverId);
}
const docker = await getRemoteDocker(serverId);
// Use the same configuration as mechanizeDockerContainer

View File

@@ -67,7 +67,7 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
await execAsync(cleanupCommand);
await execAsync(
`rsync -a --ignore-errors ${BASE_PATH}/ ${tempDir}/filesystem/`,
`rsync -a --ignore-errors --no-specials --no-devices ${BASE_PATH}/ ${tempDir}/filesystem/`,
);
writeStream.write("Copied filesystem to temp directory\n");