Bridge terraform-provider-dokploy into a Pulumi provider

18 resources and 5 data sources mapped into the dokploy:index module, with
SDKs generated for TypeScript, Python, Go and .NET.
This commit is contained in:
2026-08-08 15:28:16 +03:00
commit bb3c15135b
175 changed files with 61774 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
name: dokploy-complete
runtime: nodejs
description: A complete Dokploy environment built with Pulumi
config:
dokploy-complete:githubProviderId:
description: ID of the GitHub provider configured in Dokploy
dokploy-complete:adminPassword:
description: Basic-auth password for the staging admin tool
secret: true

View File

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

View File

@@ -0,0 +1,12 @@
{
"name": "dokploy-complete",
"devDependencies": {
"@types/node": "^18.19.130",
"typescript": "^5.9.3"
},
"dependencies": {
"@maxvojtkov/pulumi-dokploy": "file:../../sdk/nodejs",
"@pulumi/pulumi": "^3.256.0",
"@pulumi/random": "^4.21.1"
}
}

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": ["index.ts", "webService.ts"]
}

View File

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