Point at dokploy-deploy-action for rollouts

This commit is contained in:
2026-08-09 03:38:24 +03:00
parent 096e5c0974
commit c06d3386a6
4 changed files with 2041 additions and 133 deletions

View File

@@ -49,6 +49,7 @@ new dokploy.Domain("api", {
- [Installing](#installing) - [Installing](#installing)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Resources and data sources](#resources-and-data-sources) - [Resources and data sources](#resources-and-data-sources)
- [Deploying from CI](#deploying-from-ci)
- [Differences from the Terraform provider](#differences-from-the-terraform-provider) - [Differences from the Terraform provider](#differences-from-the-terraform-provider)
- [Examples](#examples) - [Examples](#examples)
- [Development](#development) - [Development](#development)
@@ -123,6 +124,30 @@ pulumi config set --secret dokploy:apiKey "$DOKPLOY_API_KEY"
Data sources: `getProject`, `getProjects`, `getEnvironment`, `getApplication`, Data sources: `getProject`, `getProjects`, `getEnvironment`, `getApplication`,
`getServers`. `getServers`.
## Deploying from CI
Like the Terraform provider, this one manages *configuration*, not *rollouts*.
Creating an `Application` writes its definition; it does not build or start
anything.
[`dokploy-deploy-action`](https://github.com/maxvojtkov/dokploy-deploy-action)
covers the other half on GitHub Actions and Gitea Actions: it triggers a
deployment, waits for the build, and fails the job when the deployment fails.
Export the application id from your stack and hand it over.
```ts
export const apiApplicationId = api.id;
```
```yaml
- uses: maxvojtkov/dokploy-deploy-action@v1
with:
host: ${{ secrets.DOKPLOY_HOST }}
api-key: ${{ secrets.DOKPLOY_API_KEY }}
application-id: ${{ steps.stack.outputs.apiApplicationId }}
docker-image: ghcr.io/acme/api:${{ github.sha }}
```
## Differences from the Terraform provider ## Differences from the Terraform provider
Everything is a mechanical translation of the Terraform provider, with three Everything is a mechanical translation of the Terraform provider, with three

View File

@@ -18,46 +18,52 @@ const adminPassword = config.requireSecret("adminPassword");
// ---------------------------------------------------------------- structure // ---------------------------------------------------------------- structure
const shop = new dokploy.Project("shop", { const shop = new dokploy.Project("shop", {
name: "shop", name: "shop",
description: "Storefront and its backing services", description: "Storefront and its backing services",
// Shared by every service in the project. // Shared by every service in the project.
env: "TZ=Europe/Bucharest", env: "TZ=Europe/Bucharest",
}); });
// Dokploy creates a default "production" environment with the project; add a // Dokploy creates a default "production" environment with the project; add a
// second one for staging. // second one for staging.
const staging = new dokploy.Environment("staging", { const staging = new dokploy.Environment("staging", {
name: "staging", name: "staging",
description: "Pre-production", description: "Pre-production",
projectId: shop.id, projectId: shop.id,
env: "LOG_LEVEL=debug", env: "LOG_LEVEL=debug",
}); });
// ------------------------------------------------------------------ secrets // ------------------------------------------------------------------ secrets
const postgresPassword = new random.RandomPassword("postgres", { length: 32, special: false }); const postgresPassword = new random.RandomPassword("postgres", {
const redisPassword = new random.RandomPassword("redis", { length: 32, special: false }); length: 32,
special: false,
});
const redisPassword = new random.RandomPassword("redis", {
length: 32,
special: false,
});
// --------------------------------------------------------------- datastores // --------------------------------------------------------------- datastores
const db = new dokploy.Postgres("db", { const db = new dokploy.Postgres("db", {
name: "shop-db", name: "shop-db",
environmentId: shop.defaultEnvironmentId, environmentId: shop.defaultEnvironmentId,
dockerImage: "postgres:16-alpine", dockerImage: "postgres:16-alpine",
databaseName: "shop", databaseName: "shop",
databaseUser: "shop", databaseUser: "shop",
databasePassword: postgresPassword.result, databasePassword: postgresPassword.result,
memoryLimit: "1g", memoryLimit: "1g",
cpuLimit: "1", cpuLimit: "1",
memoryReservation: "256m", memoryReservation: "256m",
}); });
const cache = new dokploy.Redis("cache", { const cache = new dokploy.Redis("cache", {
name: "shop-cache", name: "shop-cache",
environmentId: shop.defaultEnvironmentId, environmentId: shop.defaultEnvironmentId,
dockerImage: "redis:7-alpine", dockerImage: "redis:7-alpine",
databasePassword: redisPassword.result, databasePassword: redisPassword.result,
memoryLimit: "256m", memoryLimit: "256m",
}); });
// ----------------------------------------------------------- the web service // ----------------------------------------------------------- the web service
@@ -65,63 +71,65 @@ const cache = new dokploy.Redis("cache", {
// Dokploy resolves service names on the shared Docker network, so the database // Dokploy resolves service names on the shared Docker network, so the database
// is reachable by its generated appName. // is reachable by its generated appName.
const storefrontEnv = pulumi const storefrontEnv = pulumi
.all([db.appName, postgresPassword.result, cache.appName, redisPassword.result]) .all([db.appName, postgresPassword.result, cache.appName, redisPassword.result])
.apply(([dbHost, dbPass, cacheHost, cachePass]) => [ .apply(([dbHost, dbPass, cacheHost, cachePass]) =>
`DATABASE_URL=postgresql://shop:${dbPass}@${dbHost}:5432/shop`, [
`REDIS_URL=redis://:${cachePass}@${cacheHost}:6379`, `DATABASE_URL=postgresql://shop:${dbPass}@${dbHost}:5432/shop`,
"NODE_ENV=production", `REDIS_URL=redis://:${cachePass}@${cacheHost}:6379`,
].join("\n")); "NODE_ENV=production",
].join("\n"),
);
const storefront = new WebService("storefront", { const storefront = new WebService("storefront", {
environmentId: shop.defaultEnvironmentId, environmentId: shop.defaultEnvironmentId,
description: "Public storefront", description: "Public storefront",
github: { github: {
githubId: githubProviderId, githubId: githubProviderId,
owner: "acme", owner: "acme",
repository: "storefront", repository: "storefront",
branch: "main", branch: "main",
}, },
env: storefrontEnv, env: storefrontEnv,
replicas: 2, replicas: 2,
memoryLimit: "1g", memoryLimit: "1g",
cpuLimit: "1", cpuLimit: "1",
domains: [{ host: "shop.example.com", port: 3000 }], domains: [{ host: "shop.example.com", port: 3000 }],
}); });
new dokploy.Redirect("storefront-www", { new dokploy.Redirect("storefront-www", {
applicationId: storefront.application.id, applicationId: storefront.application.id,
regex: String.raw`^https://www\.shop\.example\.com/(.*)`, regex: String.raw`^https://www\.shop\.example\.com/(.*)`,
replacement: "https://shop.example.com/${1}", replacement: "https://shop.example.com/${1}",
permanent: true, permanent: true,
}); });
new dokploy.Mount("storefront-uploads", { new dokploy.Mount("storefront-uploads", {
serviceId: storefront.application.id, serviceId: storefront.application.id,
serviceType: "application", serviceType: "application",
type: "volume", type: "volume",
mountPath: "/app/uploads", mountPath: "/app/uploads",
volumeName: "storefront-uploads", volumeName: "storefront-uploads",
}); });
// ------------------------------- an internal tool behind basic auth (staging) // ------------------------------- an internal tool behind basic auth (staging)
new WebService("admin", { new WebService("admin", {
environmentId: staging.id, environmentId: staging.id,
dockerImage: "traefik/whoami:latest", dockerImage: "traefik/whoami:latest",
domains: [{ host: "admin.staging.example.com", port: 80 }], domains: [{ host: "admin.staging.example.com", port: 80 }],
basicAuth: { username: "ops", password: adminPassword }, basicAuth: { username: "ops", password: adminPassword },
}); });
// -------------------------------------------------------------- compose stack // -------------------------------------------------------------- compose stack
const observability = new dokploy.Compose("observability", { const observability = new dokploy.Compose("observability", {
name: "observability", name: "observability",
environmentId: shop.defaultEnvironmentId, environmentId: shop.defaultEnvironmentId,
composeType: "docker-compose", composeType: "docker-compose",
sourceType: "raw", sourceType: "raw",
// Preserve volumes if this stack is ever destroyed. // Preserve volumes if this stack is ever destroyed.
deleteVolumes: false, deleteVolumes: false,
composeFile: `services: composeFile: `services:
uptime: uptime:
image: louislam/uptime-kuma:1 image: louislam/uptime-kuma:1
volumes: volumes:
@@ -134,13 +142,13 @@ volumes:
}); });
new dokploy.Domain("observability", { new dokploy.Domain("observability", {
composeId: observability.id, composeId: observability.id,
domainType: "compose", domainType: "compose",
serviceName: "uptime", serviceName: "uptime",
host: "status.example.com", host: "status.example.com",
port: 3001, port: 3001,
https: true, https: true,
certificateType: "letsencrypt", certificateType: "letsencrypt",
}); });
export const projectId = shop.id; export const projectId = shop.id;

1850
examples/typescript/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,33 +2,36 @@ import * as pulumi from "@pulumi/pulumi";
import * as dokploy from "@maxvojtkov/pulumi-dokploy"; import * as dokploy from "@maxvojtkov/pulumi-dokploy";
export interface DomainSpec { export interface DomainSpec {
host: string; host: string;
port: number; port: number;
https?: boolean; https?: boolean;
path?: string; path?: string;
} }
export interface WebServiceArgs { export interface WebServiceArgs {
environmentId: pulumi.Input<string>; environmentId: pulumi.Input<string>;
description?: pulumi.Input<string>; description?: pulumi.Input<string>;
/** Either a prebuilt image or a GitHub repository, not both. */ /** Either a prebuilt image or a GitHub repository, not both. */
dockerImage?: pulumi.Input<string>; dockerImage?: pulumi.Input<string>;
github?: { github?: {
githubId: pulumi.Input<string>; githubId: pulumi.Input<string>;
owner: pulumi.Input<string>; owner: pulumi.Input<string>;
repository: pulumi.Input<string>; repository: pulumi.Input<string>;
branch: pulumi.Input<string>; branch: pulumi.Input<string>;
}; };
env?: pulumi.Input<string>; env?: pulumi.Input<string>;
replicas?: pulumi.Input<number>; replicas?: pulumi.Input<number>;
memoryLimit?: pulumi.Input<string>; memoryLimit?: pulumi.Input<string>;
cpuLimit?: pulumi.Input<string>; cpuLimit?: pulumi.Input<string>;
domains?: DomainSpec[]; domains?: DomainSpec[];
/** username/password pair to put the whole service behind basic auth. */ /** username/password pair to put the whole service behind basic auth. */
basicAuth?: { username: pulumi.Input<string>; password: pulumi.Input<string> }; basicAuth?: {
username: pulumi.Input<string>;
password: pulumi.Input<string>;
};
} }
/** /**
@@ -36,38 +39,53 @@ export interface WebServiceArgs {
* Pulumi equivalent of the `web-service` Terraform module. * Pulumi equivalent of the `web-service` Terraform module.
*/ */
export class WebService extends pulumi.ComponentResource { export class WebService extends pulumi.ComponentResource {
public readonly application: dokploy.Application; public readonly application: dokploy.Application;
public readonly domains: dokploy.Domain[]; public readonly domains: dokploy.Domain[];
constructor(name: string, args: WebServiceArgs, opts?: pulumi.ComponentResourceOptions) { constructor(name: string, args: WebServiceArgs, opts?: pulumi.ComponentResourceOptions) {
super("dokploy:index:WebService", name, {}, opts); super("dokploy:index:WebService", name, {}, opts);
const child = { parent: this }; const child = { parent: this };
this.application = new dokploy.Application(name, { this.application = new dokploy.Application(
name, name,
environmentId: args.environmentId, {
description: args.description, name,
env: args.env, environmentId: args.environmentId,
replicas: args.replicas, description: args.description,
memoryLimit: args.memoryLimit, env: args.env,
cpuLimit: args.cpuLimit, replicas: args.replicas,
...(args.github memoryLimit: args.memoryLimit,
? { cpuLimit: args.cpuLimit,
sourceType: "github", ...(args.github
buildType: "nixpacks", ? {
autoDeploy: true, sourceType: "github",
githubId: args.github.githubId, buildType: "nixpacks",
owner: args.github.owner, autoDeploy: true,
repository: args.github.repository, githubId: args.github.githubId,
branch: args.github.branch, owner: args.github.owner,
} repository: args.github.repository,
: { branch: args.github.branch,
sourceType: "docker", }
dockerImage: args.dockerImage, : {
}), sourceType: "docker",
}, child); dockerImage: args.dockerImage,
}),
sourceType: "gitea",
buildType: "dockerfile",
autoDeploy: true,
giteaId: "test",
owner: "test",
repository: "test",
branch: "main",
},
child,
);
this.domains = (args.domains ?? []).map((d, i) => new dokploy.Domain(`${name}-domain-${i}`, { this.domains = (args.domains ?? []).map(
(d, i) =>
new dokploy.Domain(
`${name}-domain-${i}`,
{
applicationId: this.application.id, applicationId: this.application.id,
domainType: "application", domainType: "application",
host: d.host, host: d.host,
@@ -75,19 +93,26 @@ export class WebService extends pulumi.ComponentResource {
path: d.path, path: d.path,
https: d.https ?? true, https: d.https ?? true,
certificateType: (d.https ?? true) ? "letsencrypt" : "none", certificateType: (d.https ?? true) ? "letsencrypt" : "none",
}, child)); },
child,
),
);
if (args.basicAuth) { if (args.basicAuth) {
new dokploy.Security(`${name}-auth`, { new dokploy.Security(
applicationId: this.application.id, `${name}-auth`,
username: args.basicAuth.username, {
password: args.basicAuth.password, applicationId: this.application.id,
}, child); username: args.basicAuth.username,
} password: args.basicAuth.password,
},
this.registerOutputs({ child,
application: this.application, );
domains: this.domains,
});
} }
this.registerOutputs({
application: this.application,
domains: this.domains,
});
}
} }