Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/dokploy/__test__/drop/drop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ const baseApp: ApplicationNested = {
dockerContextPath: null,
rollbackActive: false,
stopGracePeriodSwarm: null,
shmSize: null,
ulimitsSwarm: null,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const ENTERPRISE_RESOURCES = [
"domain",
"destination",
"notification",
"tag",
"logs",
"monitoring",
"auditLog",
Expand Down
44 changes: 44 additions & 0 deletions apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ type MockCreateServiceOptions = {
ContainerSpec?: {
StopGracePeriod?: number;
Ulimits?: Array<{ Name: string; Soft: number; Hard: number }>;
Mounts?: Array<{
Target: string;
Source?: string;
Type: string;
TmpfsOptions?: { SizeBytes: number; Mode: number };
}>;
};
};
[key: string]: unknown;
Expand Down Expand Up @@ -58,6 +64,7 @@ const createApplication = (
},
replicas: 1,
stopGracePeriodSwarm: 0n,
shmSize: null,
ulimitsSwarm: null,
serverId: "server-id",
...overrides,
Expand Down Expand Up @@ -158,4 +165,41 @@ describe("mechanizeDockerContainer", () => {
const [settings] = call;
expect(settings.TaskTemplate?.ContainerSpec).not.toHaveProperty("Ulimits");
});

it("adds a tmpfs mount at /dev/shm when shmSize is set", async () => {
const application = createApplication({ shmSize: "6442450944" });

await mechanizeDockerContainer(application);

expect(createServiceMock).toHaveBeenCalledTimes(1);
const call = createServiceMock.mock.calls[0];
if (!call) {
throw new Error("createServiceMock should have been called once");
}
const [settings] = call;
const shmMount = settings.TaskTemplate?.ContainerSpec?.Mounts?.find(
(m) => m.Target === "/dev/shm",
);
expect(shmMount).toBeDefined();
expect(shmMount?.Type).toBe("tmpfs");
expect(shmMount?.TmpfsOptions?.SizeBytes).toBe(6442450944);
expect(shmMount?.TmpfsOptions?.Mode).toBe(0o1777);
});

it("does not add a /dev/shm mount when shmSize is null", async () => {
const application = createApplication({ shmSize: null });

await mechanizeDockerContainer(application);

expect(createServiceMock).toHaveBeenCalledTimes(1);
const call = createServiceMock.mock.calls[0];
if (!call) {
throw new Error("createServiceMock should have been called once");
}
const [settings] = call;
const shmMount = settings.TaskTemplate?.ContainerSpec?.Mounts?.find(
(m) => m.Target === "/dev/shm",
);
expect(shmMount).toBeUndefined();
});
});
1 change: 1 addition & 0 deletions apps/dokploy/__test__/traefik/traefik.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const baseApp: ApplicationNested = {
username: null,
dockerContextPath: null,
stopGracePeriodSwarm: null,
shmSize: null,
ulimitsSwarm: null,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const addResourcesSchema = z.object({
cpuLimit: z.string().optional(),
memoryLimit: z.string().optional(),
cpuReservation: z.string().optional(),
shmSize: z.string().optional(),
ulimitsSwarm: z.array(ulimitSchema).optional(),
});

Expand Down Expand Up @@ -138,6 +139,7 @@ export const ShowResources = ({ id, type }: Props) => {
cpuReservation: "",
memoryLimit: "",
memoryReservation: "",
shmSize: "",
ulimitsSwarm: [],
},
resolver: zodResolver(addResourcesSchema),
Expand All @@ -155,6 +157,7 @@ export const ShowResources = ({ id, type }: Props) => {
cpuReservation: data?.cpuReservation || undefined,
memoryLimit: data?.memoryLimit || undefined,
memoryReservation: data?.memoryReservation || undefined,
shmSize: data?.shmSize || undefined,
ulimitsSwarm: data?.ulimitsSwarm || [],
});
}
Expand All @@ -172,6 +175,7 @@ export const ShowResources = ({ id, type }: Props) => {
cpuReservation: formData.cpuReservation || null,
memoryLimit: formData.memoryLimit || null,
memoryReservation: formData.memoryReservation || null,
shmSize: formData.shmSize || null,
ulimitsSwarm:
formData.ulimitsSwarm && formData.ulimitsSwarm.length > 0
? formData.ulimitsSwarm
Expand Down Expand Up @@ -368,6 +372,49 @@ export const ShowResources = ({ id, type }: Props) => {
/>
</div>

<div className="grid w-full md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="shmSize"
render={({ field }) => {
return (
<FormItem>
<div
className="flex items-center gap-2"
onClick={(e) => e.preventDefault()}
>
<FormLabel>SHM Size</FormLabel>
<TooltipProvider>
<Tooltip delayDuration={0}>
<TooltipTrigger>
<InfoIcon className="h-4 w-4 text-muted-foreground" />
</TooltipTrigger>
<TooltipContent>
<p>
Size of /dev/shm (shared memory) in bytes.
Default is 64MB. Use +/- buttons to adjust by
256 MB.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<FormControl>
<NumberInputWithSteps
value={field.value}
onChange={field.onChange}
placeholder="67108864 (64MB in bytes)"
step={MEMORY_STEP_MB}
converter={memoryConverter}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
</div>

{/* Ulimits Section */}
<div className="space-y-4">
<div className="flex items-center justify-between">
Expand Down
6 changes: 6 additions & 0 deletions apps/dokploy/drizzle/0153_absent_green_goblin.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ALTER TABLE "application" ADD COLUMN "shmSize" text;--> statement-breakpoint
ALTER TABLE "mariadb" ADD COLUMN "shmSize" text;--> statement-breakpoint
ALTER TABLE "mongo" ADD COLUMN "shmSize" text;--> statement-breakpoint
ALTER TABLE "mysql" ADD COLUMN "shmSize" text;--> statement-breakpoint
ALTER TABLE "postgres" ADD COLUMN "shmSize" text;--> statement-breakpoint
ALTER TABLE "redis" ADD COLUMN "shmSize" text;
Loading
Loading