Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
29 changes: 29 additions & 0 deletions examples/docker-postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Docker PostgreSQL Example

This example provisions PostgreSQL 18 Alpine with Docker resources:

- `Docker.RemoteImage` pulls `postgres:18-alpine`
- `Docker.Network` creates an app network
- `Docker.Volume` creates persistent database storage
- `Docker.Container` starts PostgreSQL with a redacted password, a network alias, a named volume, and a host port
- `Docker.Container.inspect` returns the bound host port

Docker resources use the active Docker CLI context. That can be Docker Desktop, a remote Docker host, an SSH context, or a CI runner daemon.

These resources are separate from `Cloudflare.Container`. The bridge between Docker and cloud container platforms is an image reference or registry digest, not a swappable container object.

## Commands

```sh
bun install
POSTGRES_PASSWORD=change-me bun run --filter docker-postgres-example deploy
bun run --filter docker-postgres-example destroy
```

If `POSTGRES_PASSWORD` is omitted, the example uses a redacted development default.

The stack publishes PostgreSQL on `localhost:15432`:

```sh
psql postgres://alchemy:change-me@localhost:15432/app
```
67 changes: 67 additions & 0 deletions examples/docker-postgres/alchemy.run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as Alchemy from "alchemy";
import * as Docker from "alchemy/Docker";
import * as Config from "effect/Config";
import * as Effect from "effect/Effect";
import * as Redacted from "effect/Redacted";

const POSTGRES_PORT = 15432;
const POSTGRES_CONTAINER = "alchemy-example-postgres";
const EMPTY_RUNTIME: Docker.ContainerRuntimeInfo = { ports: {} };

export default Alchemy.Stack(
"DockerPostgresExample",
{ providers: Docker.providers(), state: Alchemy.localState() },
Effect.gen(function* () {
const password = yield* Config.redacted("POSTGRES_PASSWORD").pipe(
Config.withDefault(Redacted.make("alchemy-postgres")),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use the Random resource to generate it

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


const image = yield* Docker.RemoteImage("postgres-image", {
name: "postgres",
tag: "18-alpine",
alwaysPull: false,
});
const network = yield* Docker.Network("app-network");
const data = yield* Docker.Volume("postgres-data");

const postgres = yield* Docker.Container("postgres", {
name: POSTGRES_CONTAINER,
image,
environment: {
POSTGRES_DB: "app",
POSTGRES_USER: "alchemy",
POSTGRES_PASSWORD: password,
},
ports: [{ external: POSTGRES_PORT, internal: 5432 }],
volumes: [
{
hostPath: data.name,
containerPath: "/var/lib/postgresql/data",
},
],
networks: [{ name: network.name, aliases: ["postgres"] }],
healthcheck: {
cmd: ["CMD-SHELL", "pg_isready -U alchemy -d app"],
interval: "5s",
timeout: "5s",
retries: 10,
},
start: true,
});

const runtime = yield* Docker.Container.inspect(POSTGRES_CONTAINER).pipe(
Effect.catchTag("DockerCommandError", () =>
Effect.succeed(EMPTY_RUNTIME),
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is Docker.Container.inspect? Should this be Docker.inspectContainer?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


return {
container: postgres.name,
image: image.imageRef,
network: network.name,
volume: data.name,
hostPort: runtime.ports["5432/tcp"] ?? POSTGRES_PORT,
connectionString: `postgres://alchemy:***@localhost:${POSTGRES_PORT}/app`,
};
}),
);
21 changes: 21 additions & 0 deletions examples/docker-postgres/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "docker-postgres-example",
"version": "0.0.0",
"private": true,
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/alchemy-run/alchemy-effect.git",
"directory": "examples/docker-postgres"
},
"type": "module",
"scripts": {
"deploy": "alchemy deploy",
"destroy": "alchemy destroy",
"build": "tsgo --noEmit -p tsconfig.json"
},
"dependencies": {
"alchemy": "workspace:*",
"effect": "catalog:"
}
}
16 changes: 16 additions & 0 deletions examples/docker-postgres/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"include": ["alchemy.run.ts"],
"compilerOptions": {
"noEmit": true,
"rootDir": ".",
"module": "Preserve",
"moduleResolution": "Bundler",
"target": "ESNext"
},
"references": [
{
"path": "../../packages/alchemy/tsconfig.json"
}
]
}
6 changes: 6 additions & 0 deletions packages/alchemy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@
"worker": "./src/Bundle/index.ts",
"import": "./lib/Bundle/index.js"
},
"./Docker": {
"types": "./lib/Docker/index.d.ts",
"bun": "./src/Docker/index.ts",
"worker": "./src/Docker/index.ts",
"import": "./lib/Docker/index.js"
},
"./ContentType": {
"types": "./lib/ContentType.d.ts",
"bun": "./src/ContentType.ts",
Expand Down
Loading