Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4bf97b7
feat(db): add network table for custom docker networks
chichi13 Oct 10, 2025
afb39a6
feat(db): add customNetworkIds field to all resources
chichi13 Oct 10, 2025
62ff674
feat(services): add network service layer and docker utilities
chichi13 Oct 10, 2025
67b6773
feat(api): add network tRPC router with resource filtering
chichi13 Oct 10, 2025
e00e612
feat(docker): add custom network support to resource builders
chichi13 Oct 10, 2025
a877420
feat(traefik): auto-generate Docker service labels from domains
chichi13 Oct 10, 2025
3419ed2
feat(services): integrate network management in resource services
chichi13 Oct 10, 2025
74ba1ff
feat(utils): add network support to backups and collision detection
chichi13 Oct 10, 2025
7c91b35
test: update tests to add customNetworkIds
chichi13 Oct 10, 2025
7bc272a
fix: format with biome
chichi13 Oct 10, 2025
f5fc02d
feat(ui): add shared network management components
chichi13 Oct 10, 2025
e4dced7
feat(ui): add network management to applications
chichi13 Oct 10, 2025
b9965a0
feat(ui): add network management to compose services
chichi13 Oct 10, 2025
21483e5
feat(ui): add network management to databases
chichi13 Oct 10, 2025
8237508
feat(ui): add global network management page and project tab
chichi13 Oct 10, 2025
12e3d19
feat(ui): integrate network management in navigation and pages
chichi13 Oct 10, 2025
b8e7acd
fix(server): handle null port and convert uniqueConfigKey to string i…
chichi13 Oct 10, 2025
d4cb72a
feat(network): add Traefik auto-disconnect and improve code quality
chichi13 Oct 10, 2025
d6e6174
feat: add importOrphanedNetworks + avoid code duplication
chichi13 Oct 11, 2025
65b1e71
feat(db): backward compatibility with isolated network
chichi13 Oct 11, 2025
b8f0805
refactor: better network component + remove isolated network component
chichi13 Oct 11, 2025
acc7bcc
feat(db)!: remove obsolete isolatedDeployment feature
chichi13 Oct 11, 2025
bbe1563
test: add network tests
chichi13 Oct 11, 2025
2119e30
feat(network): encrypted networks and remove attachable
chichi13 Oct 11, 2025
3bb530d
fix(network): stack compose without network should be attached to dok…
chichi13 Oct 12, 2025
7dd3d38
fix(network): network not updating for compose file
chichi13 Oct 12, 2025
3674a84
fix: network can be deleted even if an application use it
chichi13 Oct 12, 2025
7cf2635
feat(network): better error management for network creation/deletion/…
chichi13 Oct 12, 2025
b3593be
refactor(network): remove unused isDefault field from UI and API for …
chichi13 Oct 12, 2025
50209e4
fix: tests
chichi13 Oct 12, 2025
f0b1b30
feat(network): better error managerment + biome format
chichi13 Oct 12, 2025
6497378
style: better dark mode
chichi13 Oct 12, 2025
f4a0b20
fix(network): network should be detached from Traefik if no more domain
chichi13 Oct 12, 2025
507b6fa
fix: traefik service use networkId not name
chichi13 Oct 12, 2025
c5d486b
feat(network): display internal flag in network selector
chichi13 Oct 12, 2025
308fae4
style: remove duplicate BreadcrumbSidebar on network page
chichi13 Oct 17, 2025
41e66de
fix(network): deploy network on remote server
chichi13 Oct 17, 2025
0907a6a
feat(network): warn user when deploying domain on internal network
chichi13 Oct 17, 2025
d95f180
feat: better domain management with network
chichi13 Oct 24, 2025
81f5578
feat(network): add preview network selector + previewNetworkIds in db
chichi13 Oct 24, 2025
01ce15c
fix: connect to preview selected network
chichi13 Oct 24, 2025
d6cd593
fix: migration file
chichi13 Oct 24, 2025
507d91b
fix: traefik not disconnecting when no more domain attached to it
chichi13 Oct 24, 2025
1517243
feat: handle default network for domain in backend
chichi13 Oct 24, 2025
5a705b7
fix: disconnect traefik when editing
chichi13 Oct 24, 2025
848585a
Merge remote-tracking branch 'origin/canary' into feature/network-man…
chichi13 Nov 8, 2025
6e25491
fix: migration files
chichi13 Nov 8, 2025
43a63ab
merge: integrate canary into feature/network-management
chichi13 Feb 17, 2026
3bcdbb6
Merge remote-tracking branch 'origin/canary' into feature/network-man…
chichi13 Feb 17, 2026
52235e8
fix: drizzle limitation to 100 columns per query
chichi13 Feb 17, 2026
663d37a
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 17, 2026
7041fb3
chore: remove obsolete migration and related files for network manage…
Siumauricio Feb 19, 2026
f2f7dec
Merge branch 'canary' into feature/network-management
Siumauricio Feb 19, 2026
bc710ba
feat: add network management schema and update related tables
Siumauricio Feb 19, 2026
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__/compose/domain/labels.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe("createDomainLabels", () => {
previewDeploymentId: "",
internalPath: "/",
stripPath: false,
networkId: null,
};

it("should create basic labels for web entrypoint", async () => {
Expand Down
2 changes: 2 additions & 0 deletions apps/dokploy/__test__/drop/drop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const baseApp: ApplicationNested = {
previewPort: 3000,
previewLimit: 0,
previewWildcard: "",
previewNetworkIds: null,
environment: {
env: "",
isDefault: false,
Expand Down Expand Up @@ -151,6 +152,7 @@ const baseApp: ApplicationNested = {
dockerContextPath: null,
rollbackActive: false,
stopGracePeriodSwarm: null,
customNetworkIds: null,
ulimitsSwarm: null,
};

Expand Down
309 changes: 309 additions & 0 deletions apps/dokploy/__test__/network/compose-networks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
import type { ComposeSpecification } from "@dokploy/server";
import {
addAppNameToRootNetwork,
addAppNameToServiceNetworks,
addCustomNetworksToCompose,
} from "@dokploy/server/utils/docker/collision/root-network";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { parse } from "yaml";

// Mock the database
vi.mock("@dokploy/server/db", () => ({
db: {
query: {
networks: {
findMany: vi.fn(),
},
},
},
}));

import { db } from "@dokploy/server/db";

describe("addAppNameToRootNetwork", () => {
it("adds external network to compose", () => {
const compose: ComposeSpecification = {
services: {
web: {
image: "nginx",
},
},
};

const result = addAppNameToRootNetwork(compose, "dokploy-network");

expect(result.networks).toBeDefined();
expect(result.networks?.["dokploy-network"]).toEqual({
name: "dokploy-network",
external: true,
});
});

it("preserves existing networks", () => {
const compose = parse(`
services:
web:
image: nginx
networks:
frontend:
driver: bridge
`) as ComposeSpecification;

const result = addAppNameToRootNetwork(compose, "dokploy-network");

expect(result.networks?.frontend).toBeDefined();
expect(result.networks?.["dokploy-network"]).toEqual({
name: "dokploy-network",
external: true,
});
});

it("initializes networks object if undefined", () => {
const compose: ComposeSpecification = {
services: {
web: { image: "nginx" },
},
};

const result = addAppNameToRootNetwork(compose, "test-network");

expect(result.networks).toBeDefined();
expect(result.networks?.["test-network"]).toBeDefined();
});
});

describe("addAppNameToServiceNetworks", () => {
it("adds network to services with array format", () => {
const services = {
web: {
image: "nginx",
networks: ["frontend", "backend"],
},
api: {
image: "node",
networks: ["backend"],
},
};

const result = addAppNameToServiceNetworks(services, "dokploy-network");

expect(result.web.networks).toContain("dokploy-network");
expect(result.web.networks).toContain("frontend");
expect(result.web.networks).toContain("backend");
expect(result.api.networks).toContain("dokploy-network");
expect(result.api.networks).toContain("backend");
});

it("adds network to services with object format", () => {
const services = {
web: {
image: "nginx",
networks: {
frontend: {
aliases: ["web-alias"],
},
},
},
};

const result = addAppNameToServiceNetworks(services, "dokploy-network");

expect(result.web.networks).toHaveProperty("dokploy-network");
expect(result.web.networks).toHaveProperty("frontend");
});

it("handles services without networks", () => {
const services = {
web: {
image: "nginx",
},
api: {
image: "node",
},
};

const result = addAppNameToServiceNetworks(services, "dokploy-network");

expect(result.web.networks).toEqual(["dokploy-network"]);
expect(result.api.networks).toEqual(["dokploy-network"]);
});

it("does not duplicate network if already present (array format)", () => {
const services = {
web: {
image: "nginx",
networks: ["dokploy-network", "frontend"],
},
};

const result = addAppNameToServiceNetworks(services, "dokploy-network");

const webNetworks = result.web.networks as string[];
const count = webNetworks.filter((n) => n === "dokploy-network").length;
expect(count).toBe(1);
});

it("handles mixed network formats across services", () => {
const services = {
web: {
image: "nginx",
networks: ["frontend"],
},
api: {
image: "node",
networks: {
backend: {},
},
},
worker: {
image: "worker",
},
};

const result = addAppNameToServiceNetworks(services, "dokploy-network");

expect(Array.isArray(result.web.networks)).toBe(true);
expect((result.web.networks as string[]).includes("dokploy-network")).toBe(
true,
);

expect(typeof result.api.networks).toBe("object");
expect(result.api.networks).toHaveProperty("dokploy-network");

expect(result.worker.networks).toEqual(["dokploy-network"]);
});
});

describe("addCustomNetworksToCompose (with mocked DB)", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("adds custom networks from DB to compose", async () => {
const mockNetworks = [
{ networkId: "net-1", networkName: "custom-net-1" },
{ networkId: "net-2", networkName: "custom-net-2" },
];

vi.mocked(db.query.networks.findMany).mockResolvedValue(mockNetworks);

const compose: ComposeSpecification = {
services: {
web: { image: "nginx" },
},
};

const result = await addCustomNetworksToCompose(compose, [
"net-1",
"net-2",
]);

expect(result.networks).toBeDefined();
expect(result.networks?.["custom-net-1"]).toEqual({
name: "custom-net-1",
external: true,
});
expect(result.networks?.["custom-net-2"]).toEqual({
name: "custom-net-2",
external: true,
});
});

it("adds custom networks to all services", async () => {
const mockNetworks = [{ networkId: "net-1", networkName: "prod-network" }];

vi.mocked(db.query.networks.findMany).mockResolvedValue(mockNetworks);

const compose: ComposeSpecification = {
services: {
web: { image: "nginx", networks: ["frontend"] },
api: { image: "node" },
},
};

const result = await addCustomNetworksToCompose(compose, ["net-1"]);

expect(
(result.services?.web?.networks as string[]).includes("prod-network"),
).toBe(true);
expect(
(result.services?.api?.networks as string[]).includes("prod-network"),
).toBe(true);
});

it("handles empty custom networks array", async () => {
vi.mocked(db.query.networks.findMany).mockResolvedValue([]);

const compose: ComposeSpecification = {
services: {
web: { image: "nginx" },
},
};

const result = await addCustomNetworksToCompose(compose, []);

expect(result.networks).toBeDefined();
expect(Object.keys(result.networks || {}).length).toBe(0);
});

it("preserves existing compose networks", async () => {
const mockNetworks = [{ networkId: "net-1", networkName: "custom-net" }];

vi.mocked(db.query.networks.findMany).mockResolvedValue(mockNetworks);

const compose = parse(`
services:
web:
image: nginx
networks:
frontend:
driver: bridge
`) as ComposeSpecification;

const result = await addCustomNetworksToCompose(compose, ["net-1"]);

expect(result.networks?.frontend).toBeDefined();
expect(result.networks?.["custom-net"]).toBeDefined();
});

it("initializes networks if undefined", async () => {
const mockNetworks = [{ networkId: "net-1", networkName: "test-net" }];

vi.mocked(db.query.networks.findMany).mockResolvedValue(mockNetworks);

const compose: ComposeSpecification = {
services: {
web: { image: "nginx" },
},
};

const result = await addCustomNetworksToCompose(compose, ["net-1"]);

expect(result.networks).toBeDefined();
expect(result.networks?.["test-net"]).toBeDefined();
});

it("handles services with object network format", async () => {
const mockNetworks = [{ networkId: "net-1", networkName: "custom-net" }];

vi.mocked(db.query.networks.findMany).mockResolvedValue(mockNetworks);

const compose: ComposeSpecification = {
services: {
web: {
image: "nginx",
networks: {
frontend: {
aliases: ["web"],
},
},
},
},
};

const result = await addCustomNetworksToCompose(compose, ["net-1"]);

expect(result.services?.web?.networks).toHaveProperty("custom-net");
expect(result.services?.web?.networks).toHaveProperty("frontend");
});
});
Loading
Loading