Skip to content

Commit 413ed9b

Browse files
authored
Merge pull request #3604 from Dokploy/canary
🚀 Release v0.27.0
2 parents 4f57851 + 17da1d5 commit 413ed9b

File tree

171 files changed

+51848
-3966
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+51848
-3966
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Before submitting this PR, please make sure that:
88

99
- [ ] You created a dedicated branch based on the `canary` branch.
1010
- [ ] You have read the suggestions in the CONTRIBUTING.md file https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#pull-request
11-
- [ ] You have tested this PR in your local instance.
11+
- [ ] You have tested this PR in your local instance. If you have not tested it yet, please do so before submitting. This helps avoid wasting maintainers' time reviewing code that has not been verified by you.
1212

1313
## Issues related (if applicable)
1414

CONTRIBUTING.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Hey, thanks for your interest in contributing to Dokploy! We appreciate your help and taking your time to contribute.
44

5-
Before you start, please first discuss the feature/bug you want to add with the owners and comunity via github issues.
5+
Before you start, please first discuss the feature/bug you want to add with the owners and community via github issues.
66

77
We have a few guidelines to follow when contributing to this project:
88

@@ -11,6 +11,7 @@ We have a few guidelines to follow when contributing to this project:
1111
- [Development](#development)
1212
- [Build](#build)
1313
- [Pull Request](#pull-request)
14+
- [Important Considerations](#important-considerations-for-pull-requests)
1415

1516
## Commit Convention
1617

@@ -162,8 +163,9 @@ curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.39.1/pack-v0.
162163
- If your pull request fixes an open issue, please reference the issue in the pull request description.
163164
- Once your pull request is merged, you will be automatically added as a contributor to the project.
164165

165-
**Important Considerations for Pull Requests:**
166+
### Important Considerations for Pull Requests
166167

168+
- **Testing is Mandatory:** All Pull Requests **must be tested** before submission. You must verify that your changes work as expected in a local development environment (see [Setup](#setup)). **Pull Requests that have not been tested will be closed.** This policy ensures clean contributions and reduces the time maintainers spend reviewing untested or broken code.
167169
- **Focus and Scope:** Each Pull Request should ideally address a single, well-defined problem or introduce one new feature. This greatly facilitates review and reduces the chances of introducing unintended side effects.
168170
- **Avoid Unfocused Changes:** Please avoid submitting Pull Requests that contain only minor changes such as whitespace adjustments, IDE-generated formatting, or removal of unused variables, unless these are part of a larger, clearly defined refactor or a dedicated "cleanup" Pull Request that addresses a specific `good first issue` or maintenance task.
169171
- **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`).

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,8 @@ RUN curl -sSL https://railpack.com/install.sh | bash
6565
COPY --from=buildpacksio/pack:0.39.1 /usr/local/bin/pack /usr/local/bin/pack
6666

6767
EXPOSE 3000
68-
CMD [ "pnpm", "start" ]
68+
69+
HEALTHCHECK --interval=10s --timeout=3s --retries=10 \
70+
CMD curl -fs http://localhost:3000/api/trpc/settings.health || exit 1
71+
72+
CMD ["sh", "-c", "pnpm run wait-for-postgres && exec pnpm start"]

README.md

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,8 @@
1212
<br />
1313

1414

15-
16-
<div align="center" markdown="1">
17-
<sup>Special thanks to:</sup>
18-
<br>
19-
<br>
20-
<a href="https://tuple.app/dokploy">
21-
<img src=".github/sponsors/tuple.png" alt="Tuple's sponsorship image" width="400"/>
22-
</a>
23-
24-
### [Tuple, the premier screen sharing app for developers](https://tuple.app/dokploy)
25-
[Available for MacOS & Windows](https://tuple.app/dokploy)<br>
26-
27-
</div>
28-
29-
3015
Dokploy is a free, self-hostable Platform as a Service (PaaS) that simplifies the deployment and management of applications and databases.
3116

32-
3317
## ✨ Features
3418

3519
Dokploy includes multiple features to make your life easier.
@@ -60,40 +44,9 @@ curl -sSL https://dokploy.com/install.sh | sh
6044

6145
For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
6246

63-
## ♥️ Sponsors
64-
65-
🙏 We're deeply grateful to all our sponsors who make Dokploy possible! Your support helps cover the costs of hosting, testing, and developing new features.
66-
67-
[Dokploy Open Collective](https://opencollective.com/dokploy)
6847

6948
[Github Sponsors](https://github.com/sponsors/Siumauricio)
7049

71-
## Sponsors
72-
73-
| Sponsor | Logo | Supporter Level |
74-
|---------|:----:|----------------|
75-
| [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) | <img src=".github/sponsors/hostinger.jpg" alt="Hostinger" width="200"/> | 🎖 Hero Sponsor |
76-
| [LX Aer](https://www.lxaer.com/?ref=dokploy) | <img src=".github/sponsors/lxaer.png" alt="LX Aer" width="100"/> | 🎖 Hero Sponsor |
77-
| [LinkDR](https://linkdr.com/?ref=dokploy) | <img src="https://dokploy.com/linkdr-logo.svg" alt="LinkDR" width="100"/> | 🎖 Hero Sponsor |
78-
| [LambdaTest](https://www.lambdatest.com/?utm_source=dokploy&utm_medium=sponsor) | <img src="https://www.lambdatest.com/blue-logo.png" alt="LambdaTest" width="200"/> | 🎖 Hero Sponsor |
79-
| [Awesome Tools](https://awesome.tools/) | <img src=".github/sponsors/awesome.png" alt="Awesome Tools" width="100"/> | 🎖 Hero Sponsor |
80-
| [Supafort](https://supafort.com/?ref=dokploy) | <img src="https://supafort.com/build/q-4Ht4rBZR.webp" alt="Supafort.com" width="200"/> | 🥇 Premium Supporter |
81-
| [Agentdock](https://agentdock.ai/?ref=dokploy) | <img src=".github/sponsors/agentdock.png" alt="agentdock.ai" width="100"/> | 🥇 Premium Supporter |
82-
| [AmericanCloud](https://americancloud.com/?ref=dokploy) | <img src=".github/sponsors/american-cloud.png" alt="AmericanCloud" width="200"/> | 🥈 Elite Contributor |
83-
| [Tolgee](https://tolgee.io/?utm_source=github_dokploy&utm_medium=banner&utm_campaign=dokploy) | <img src="https://dokploy.com/tolgee-logo.png" alt="Tolgee" width="100"/> | 🥈 Elite Contributor |
84-
| [Cloudblast](https://cloudblast.io/?ref=dokploy) | <img src="https://cloudblast.io/img/logo-icon.193cf13e.svg" alt="Cloudblast.io" width="150"/> | 🥉 Supporting Member |
85-
| [Synexa](https://synexa.ai/?ref=dokploy) | <img src=".github/sponsors/synexa.png" alt="Synexa" width="100"/> | 🥉 Supporting Member |
86-
87-
### Community Backers 🤝
88-
89-
#### Organizations:
90-
91-
[Sponsors on Open Collective](https://opencollective.com/dokploy)
92-
93-
#### Individuals:
94-
95-
[![Individual Contributors on Open Collective](https://opencollective.com/dokploy/individuals.svg?width=890)](https://opencollective.com/dokploy)
96-
9750
### Contributors 🤝
9851

9952
<a href="https://github.com/dokploy/dokploy/graphs/contributors">

apps/api/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@hono/node-server": "^1.14.3",
1515
"@hono/zod-validator": "0.3.0",
1616
"dotenv": "^16.4.5",
17-
"hono": "^4.7.10",
17+
"hono": "^4.11.7",
1818
"pino": "9.4.0",
1919
"pino-pretty": "11.2.2",
2020
"react": "18.2.0",
@@ -23,7 +23,7 @@
2323
"zod": "^3.25.32"
2424
},
2525
"devDependencies": {
26-
"@types/node": "^20.17.51",
26+
"@types/node": "^20.16.0",
2727
"@types/react": "^18.2.37",
2828
"@types/react-dom": "^18.2.15",
2929
"tsx": "^4.16.2",

apps/dokploy/__test__/compose/domain/network-service.test.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,30 @@ import { describe, expect, it } from "vitest";
44
describe("addDokployNetworkToService", () => {
55
it("should add network to an empty array", () => {
66
const result = addDokployNetworkToService([]);
7-
expect(result).toEqual(["dokploy-network"]);
7+
expect(result).toEqual(["dokploy-network", "default"]);
88
});
99

1010
it("should not add duplicate network to an array", () => {
1111
const result = addDokployNetworkToService(["dokploy-network"]);
12-
expect(result).toEqual(["dokploy-network"]);
12+
expect(result).toEqual(["dokploy-network", "default"]);
1313
});
1414

1515
it("should add network to an existing array with other networks", () => {
1616
const result = addDokployNetworkToService(["other-network"]);
17-
expect(result).toEqual(["other-network", "dokploy-network"]);
17+
expect(result).toEqual(["other-network", "dokploy-network", "default"]);
1818
});
1919

2020
it("should add network to an object if networks is an object", () => {
2121
const result = addDokployNetworkToService({ "other-network": {} });
22-
expect(result).toEqual({ "other-network": {}, "dokploy-network": {} });
22+
expect(result).toEqual({
23+
"other-network": {},
24+
"dokploy-network": {},
25+
default: {},
26+
});
27+
});
28+
29+
it("should not duplicate default network when already present", () => {
30+
const result = addDokployNetworkToService(["default", "dokploy-network"]);
31+
expect(result).toEqual(["default", "dokploy-network"]);
2332
});
2433
});

apps/dokploy/__test__/drop/drop.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ const baseApp: ApplicationNested = {
147147
dockerContextPath: null,
148148
rollbackActive: false,
149149
stopGracePeriodSwarm: null,
150+
ulimitsSwarm: null,
150151
};
151152

152153
describe("unzipDrop using real zip files", () => {

apps/dokploy/__test__/server/mechanizeDockerContainer.test.ts

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ type MockCreateServiceOptions = {
66
TaskTemplate?: {
77
ContainerSpec?: {
88
StopGracePeriod?: number;
9+
Ulimits?: Array<{ Name: string; Soft: number; Hard: number }>;
910
};
1011
};
1112
[key: string]: unknown;
1213
};
1314

1415
const { inspectMock, getServiceMock, createServiceMock, getRemoteDockerMock } =
1516
vi.hoisted(() => {
16-
const inspect = vi.fn<[], Promise<never>>();
17+
const inspect = vi.fn<() => Promise<never>>();
1718
const getService = vi.fn(() => ({ inspect }));
18-
const createService = vi.fn<[MockCreateServiceOptions], Promise<void>>(
19-
async () => undefined,
20-
);
19+
const createService = vi.fn<
20+
(opts: MockCreateServiceOptions) => Promise<void>
21+
>(async () => undefined);
2122
const getRemoteDocker = vi.fn(async () => ({
2223
getService,
2324
createService,
@@ -57,6 +58,7 @@ const createApplication = (
5758
},
5859
replicas: 1,
5960
stopGracePeriodSwarm: 0n,
61+
ulimitsSwarm: null,
6062
serverId: "server-id",
6163
...overrides,
6264
}) as unknown as ApplicationNested;
@@ -80,7 +82,9 @@ describe("mechanizeDockerContainer", () => {
8082
await mechanizeDockerContainer(application);
8183

8284
expect(createServiceMock).toHaveBeenCalledTimes(1);
83-
const call = createServiceMock.mock.calls[0];
85+
const call = createServiceMock.mock.calls[0] as
86+
| [MockCreateServiceOptions]
87+
| undefined;
8488
if (!call) {
8589
throw new Error("createServiceMock should have been called once");
8690
}
@@ -97,7 +101,9 @@ describe("mechanizeDockerContainer", () => {
97101
await mechanizeDockerContainer(application);
98102

99103
expect(createServiceMock).toHaveBeenCalledTimes(1);
100-
const call = createServiceMock.mock.calls[0];
104+
const call = createServiceMock.mock.calls[0] as
105+
| [MockCreateServiceOptions]
106+
| undefined;
101107
if (!call) {
102108
throw new Error("createServiceMock should have been called once");
103109
}
@@ -106,4 +112,50 @@ describe("mechanizeDockerContainer", () => {
106112
"StopGracePeriod",
107113
);
108114
});
115+
116+
it("passes ulimits to ContainerSpec when ulimitsSwarm is defined", async () => {
117+
const ulimits = [
118+
{ Name: "nofile", Soft: 10000, Hard: 20000 },
119+
{ Name: "nproc", Soft: 4096, Hard: 8192 },
120+
];
121+
const application = createApplication({ ulimitsSwarm: ulimits });
122+
123+
await mechanizeDockerContainer(application);
124+
125+
expect(createServiceMock).toHaveBeenCalledTimes(1);
126+
const call = createServiceMock.mock.calls[0];
127+
if (!call) {
128+
throw new Error("createServiceMock should have been called once");
129+
}
130+
const [settings] = call;
131+
expect(settings.TaskTemplate?.ContainerSpec?.Ulimits).toEqual(ulimits);
132+
});
133+
134+
it("omits Ulimits when ulimitsSwarm is null", async () => {
135+
const application = createApplication({ ulimitsSwarm: null });
136+
137+
await mechanizeDockerContainer(application);
138+
139+
expect(createServiceMock).toHaveBeenCalledTimes(1);
140+
const call = createServiceMock.mock.calls[0];
141+
if (!call) {
142+
throw new Error("createServiceMock should have been called once");
143+
}
144+
const [settings] = call;
145+
expect(settings.TaskTemplate?.ContainerSpec).not.toHaveProperty("Ulimits");
146+
});
147+
148+
it("omits Ulimits when ulimitsSwarm is an empty array", async () => {
149+
const application = createApplication({ ulimitsSwarm: [] });
150+
151+
await mechanizeDockerContainer(application);
152+
153+
expect(createServiceMock).toHaveBeenCalledTimes(1);
154+
const call = createServiceMock.mock.calls[0];
155+
if (!call) {
156+
throw new Error("createServiceMock should have been called once");
157+
}
158+
const [settings] = call;
159+
expect(settings.TaskTemplate?.ContainerSpec).not.toHaveProperty("Ulimits");
160+
});
109161
});

apps/dokploy/__test__/setup.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { vi } from "vitest";
2+
3+
/**
4+
* Mock the DB module so tests that import from @dokploy/server (barrel)
5+
* never open a real TCP connection to PostgreSQL (e.g. in CI where no DB runs).
6+
* Without this, loading the server barrel pulls in lib/auth and db, which
7+
* connect to localhost:5432 and cause ECONNREFUSED.
8+
*/
9+
vi.mock("@dokploy/server/db", () => {
10+
const chain = () => chain;
11+
chain.set = () => chain;
12+
chain.where = () => chain;
13+
chain.values = () => chain;
14+
chain.returning = () => Promise.resolve([{}]);
15+
chain.then = undefined;
16+
17+
const tableMock = {
18+
findFirst: vi.fn(() => Promise.resolve(undefined)),
19+
findMany: vi.fn(() => Promise.resolve([])),
20+
insert: vi.fn(() => Promise.resolve([{}])),
21+
update: vi.fn(() => chain),
22+
delete: vi.fn(() => chain),
23+
};
24+
const createQueryMock = () => tableMock;
25+
26+
return {
27+
db: {
28+
select: vi.fn(() => chain),
29+
insert: vi.fn(() => ({
30+
values: () => ({ returning: () => Promise.resolve([{}]) }),
31+
})),
32+
update: vi.fn(() => chain),
33+
delete: vi.fn(() => chain),
34+
query: new Proxy({} as Record<string, typeof tableMock>, {
35+
get: () => tableMock,
36+
}),
37+
},
38+
dbUrl: "postgres://mock:mock@localhost:5432/mock",
39+
};
40+
});

apps/dokploy/__test__/traefik/traefik.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ const baseApp: ApplicationNested = {
125125
username: null,
126126
dockerContextPath: null,
127127
stopGracePeriodSwarm: null,
128+
ulimitsSwarm: null,
128129
};
129130

130131
const baseDomain: Domain = {

0 commit comments

Comments
 (0)