# Dev Containers

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/examples/dev/devcontainer>.

If your repository already has a [Development Container](https://containers.dev/) configuration, for example for VS Code Dev Containers or GitHub Codespaces, Cordium can use it as is: it builds the image, installs the features, runs the lifecycle commands and applies the environment variables of `devcontainer.json`, with no Cordium-specific configuration at all (read more [here](https://octelium.com/docs/cordium/latest/workspaces/image.md#devcontainers)). This makes Cordium a self-hosted alternative to hosted development environments for teams that already invested in Dev Containers.

## The Repository

Here is the `.devcontainer/devcontainer.json` of a Go and TypeScript repository:

```json
{
  "name": "Ledger",
  "build": {
    "dockerfile": "Dockerfile",
    "context": "..",
    "args": {
      "GO_VERSION": "1.26"
    }
  },
  "features": {
    "ghcr.io/devcontainers/features/node:1": {
      "version": "24"
    },
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "containerEnv": {
    "APP_ENV": "development",
    "GOFLAGS": "-buildvcs=false"
  },
  "onCreateCommand": "make tools",
  "postCreateCommand": "go mod download && npm ci --prefix web",
  "postStartCommand": "make dev-config",
  "customizations": {
    "vscode": {
      "extensions": ["golang.go", "dbaeumer.vscode-eslint"]
    }
  }
}
```

And its `.devcontainer/Dockerfile`:

```dockerfile
ARG GO_VERSION=1.26
FROM golang:${GO_VERSION}-bookworm
RUN apt-get update && apt-get install -y --no-install-recommends \
      postgresql-client make jq \
    && rm -rf /var/lib/apt/lists/*
```

## Running It in Cordium

Since Cordium finds the devcontainer spec in the repository when the image is not explicitly set, the repository URL is all you need for a public repository:

```bash
cordium run --space payments.cordium --repository https://github.com/acme-corp/ledger
```

For a team, a *Template* is more convenient, since it can also define the repository's credentials, resource limits and pre-builds. The image is explicitly built from the repository's devcontainer spec:

```yaml
spec:
  repository:
    url: https://github.com/acme-corp/ledger
    authentication:
      http:
        username: x-access-token
        password:
          fromSecret: github-read-token.payments.cordium
  image:
    repository:
      devcontainer:
        dirPath: .devcontainer
  limit:
    cpu:
      millicores: 4000
    memory:
      megabytes: 8192
```

```bash
cordium create template ledger.payments.cordium --file ledger.yaml
cordium build ledger.payments.cordium
```

The `onCreateCommand` and `postCreateCommand` run as `ON_CREATE` tasks, which means that they run once in the pre-build, while `postStartCommand` runs as a `POST_START` task on every start. All of them run in `/workspace/repo`.

## Supported Properties

Cordium supports the following `devcontainer.json` properties:

| Property                                                       | Behavior in Cordium                                                                                                                                                                                                                                    |
| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `image`, `build.dockerfile`, `build.context`, `build.args`     | The image is pulled or built inside the *Workspace*'s pod with rootless Podman.                                                                                                                                                                        |
| `dockerComposeFile`, `service`                                 | The image of the given service is built. The other services of the Compose file are not started. Start them via Podman in a lifecycle command or task instead (read more [here](https://octelium.com/docs/cordium/latest/examples/dev/containers.md)). |
| `features`                                                     | Downloaded and installed on fresh runs. You can also add features to any *Workspace* via `spec.runtime.devcontainers.features`.                                                                                                                        |
| `containerEnv`                                                 | Injected as environment variables.                                                                                                                                                                                                                     |
| `onCreateCommand`, `updateContentCommand`, `postCreateCommand` | Run as `ON_CREATE` tasks.                                                                                                                                                                                                                              |
| `postStartCommand`                                             | Run as a `POST_START` task.                                                                                                                                                                                                                            |
| `overrideCommand`, `init`                                      | Honored for the container's command and init process.                                                                                                                                                                                                  |
| `customizations.vscode.extensions`                             | Installed when the image provides a `code`, `code-server` or `openvscode-server` binary.                                                                                                                                                               |

The properties that relate to the local Docker engine, such as `forwardPorts`, `mounts`, `runArgs` and `privileged`, are ignored. Use Cordium's own equivalents instead: applications for ports (read more [here](https://octelium.com/docs/cordium/latest/workspaces/applications.md)), *Volumes* for mounts (read more [here](https://octelium.com/docs/cordium/latest/workspaces/volumes.md)), and capabilities for privileges (read more [here](https://octelium.com/docs/cordium/latest/workspaces/runtime.md#filesystem-and-capabilities)).

## Adding Cordium-Specific Configuration

Configuration that has no devcontainer equivalent, such as secretless access to Octelium *Services* or Cordium tasks that need it, can live next to `devcontainer.json` in a `.cordium.yaml` file at the root of the repository, which Cordium merges into the configuration of every *Workspace* of the repository (read more [here](https://octelium.com/docs/cordium/latest/workspaces/overview.md#repository-configuration-files)):

```yaml
spec:
  runtime:
    envVars:
      - key: DATABASE_URL
        value: postgres://pg-staging.payments:5432/ledger
    tasks:
      - name: migrate
        type: POST_START
        workingDir: /workspace/repo
        run: |
          timeout 120 sh -c 'until getent hosts pg-staging.payments > /dev/null; do sleep 2; done'
          make migrate
```

Developers who still use VS Code Dev Containers locally are unaffected, since Docker and VS Code ignore this file.
