# Running Containers

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

Cordium Workspaces can run nested containers with Podman. This makes it possible to use a Workspace not only as a shell environment, but also as a complete development or automation environment capable of running databases, web servers, etc. Here is a rather detailed example:

```yaml
spec:
  image:
    registry:
      url: golang:1.23-bookworm

  repository:
    url: https://github.com/example/data-platform
    cloneOptions:
      branch: main
      depth: 1

  runtime:
    envVars:
      - key: POSTGRES_DSN
        value: postgres://postgres:password@localhost:5432/app?sslmode=disable
      - key: MYSQL_DSN
        value: root:password@tcp(localhost:3306)/app
      - key: REDIS_ADDR
        value: localhost:6379
      - key: CLICKHOUSE_DSN
        value: clickhouse://octelium:octelium@localhost:9000/default

    tasks:
      - name: install-tools
        type: ON_CREATE
        runAsRoot: true
        run: |
          apt-get update
          apt-get install -y \
            podman \
            git \
            curl \
            jq \
            postgresql-client \
            default-mysql-client \
            redis-tools \
            build-essential \
            && rm -rf /var/lib/apt/lists/*

      - name: download-go-dependencies
        type: ON_CREATE
        workingDir: /workspace/repo
        run: |
          go mod download
        onFailure: ON_FAILURE_ABORT

      - name: start-postgres
        type: POST_START
        run: |
          sudo podman rm -f cordium-postgres 2>/dev/null || true

          sudo podman run \
            --name cordium-postgres \
            --net host \
            -e POSTGRES_PASSWORD=password \
            -e POSTGRES_DB=app \
            -d docker.io/postgres:16

          until pg_isready -h localhost -p 5432 -U postgres; do
            sleep 1
          done

      - name: start-mariadb
        type: POST_START
        run: |
          sudo podman rm -f cordium-mariadb 2>/dev/null || true

          sudo podman run \
            --name cordium-mariadb \
            --net host \
            -e MARIADB_ROOT_PASSWORD=password \
            -e MARIADB_DATABASE=app \
            -d docker.io/mariadb:11

          until mysqladmin ping -h 127.0.0.1 -P 3306 -uroot -ppassword --silent; do
            sleep 1
          done

      - name: start-redis
        type: POST_START
        run: |
          sudo podman rm -f cordium-redis 2>/dev/null || true

          sudo podman run \
            --name cordium-redis \
            --net host \
            -d docker.io/redis:7

          until redis-cli -h localhost ping | grep -q PONG; do
            sleep 1
          done

      - name: start-clickhouse
        type: POST_START
        run: |
          sudo podman rm -f cordium-clickhouse 2>/dev/null || true

          sudo podman run \
            --name cordium-clickhouse \
            --net host \
            -e CLICKHOUSE_USER=octelium \
            -e CLICKHOUSE_PASSWORD=octelium \
            -d docker.io/clickhouse/clickhouse-server:latest

          until curl -fsS http://localhost:8123/ping | grep -q Ok; do
            sleep 1
          done

      - name: run-integration-tests
        type: POST_START
        workingDir: /workspace/repo
        run: |
          go test ./... -count=1
        onFailure: ON_FAILURE_ABORT

      - name: start-service
        type: POST_START
        workingDir: /workspace/repo
        isBackground: true
        run: |
          go run ./cmd/server

      - name: stop-containers
        type: PRE_STOP
        run: |
          sudo podman rm -f \
            cordium-postgres \
            cordium-mariadb \
            cordium-redis \
            cordium-clickhouse \
            2>/dev/null || true

  limit:
    cpu:
      millicores: 8000
    memory:
      megabytes: 16384
    storage:
      megabytes: 50000
```

You can also use Cordium to build Docker/OCI images and push them to registries (e.g. ghcr). Here is an example:

```yaml
spec:
  vars:
    - name: REPO_URL
      value: https://github.com/myorg/my-service
    - name: BRANCH
      value: main
    - name: IMAGE
      value: ghcr.io/myorg/my-service
    - name: TAG
      value: latest
    - name: DOCKERFILE
      value: Dockerfile
    - name: CONTEXT
      value: .

  image:
    registry:
      url: ubuntu:24.04

  repository:
    url: ${{ vars.REPO_URL }}
    cloneOptions:
      branch: ${{ vars.BRANCH }}
      depth: 1
      singleBranch: true
      disableLazyUnshallow: true

  runtime:
    envVars:
      - key: REGISTRY
        value: ghcr.io
      - key: REGISTRY_USERNAME
        value: myorg
      - key: REGISTRY_TOKEN
        fromSecret: ghcr-token

    tasks:
      - name: install-podman
        type: ON_CREATE
        runAsRoot: true
        run: |
          apt-get update
          apt-get install -y \
            ca-certificates \
            curl \
            git \
            jq \
            podman \
            buildah \
            skopeo \
            && rm -rf /var/lib/apt/lists/*

      - name: registry-login
        type: ON_CREATE
        run: |
          echo "${REGISTRY_TOKEN}" | sudo podman login "${REGISTRY}" \
            --username "${REGISTRY_USERNAME}" \
            --password-stdin

      - name: build-image
        type: ON_CREATE
        workingDir: /workspace/repo
        run: |
          sudo podman build \
            --pull=always \
            --file "${{ vars.DOCKERFILE }}" \
            --tag "${{ vars.IMAGE }}:${{ vars.TAG }}" \
            "${{ vars.CONTEXT }}"

      - name: push-image
        type: ON_CREATE
        run: |
          sudo podman push "${{ vars.IMAGE }}:${{ vars.TAG }}"

      - name: inspect-pushed-image
        type: ON_CREATE
        run: |
          skopeo inspect "docker://${{ vars.IMAGE }}:${{ vars.TAG }}" | jq .

    autoStop: true

  limit:
    cpu:
      millicores: 4000
    memory:
      megabytes: 8192
    storage:
      megabytes: 30000
```
