Sign inSign up

dockersamples/labspace-containerized-sdlc:latest

Manifest digest

sha256:00478784a286a71a49b09ac606f289dfc763eef996ca4e317ca1037df7cf0e66

Last pushed

4 months by dockersamplesbuildbot

Type

Compose

Manifest digest

sha256:00478784a286a71a49b09ac606f289dfc763eef996ca4e317ca1037df7cf0e66

Compose file content

##################################################################################################
# This Compose file is intended to run a Labspace. It can be run with the following:
#
# CONTENT_REPO_URL=https://github.com/username/labspace-content-repo docker compose -f compose.run.yaml up
##################################################################################################

services:
  configurator:
    image: dockersamples/labspace-configurator:v0.27.1
    use_api_socket: true
    volumes:
      - labspace-content:/project
      - labspace-instructions:/instructions
      - docker-creds:/docker-creds
      - labspace-support:/etc/labspace-support

  interface:
    image: dockersamples/labspace-interface:v0.27.1
    ports:
      - "3030:3030"
    volumes:
      - labspace-instructions:/labspace/instructions:ro
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/private-key
        volume:
          subpath: private-key
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/socket
        volume:
          subpath: socket
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/metadata
        volume:
          subpath: metadata
    depends_on:
      workspace:
        condition: service_started
      configurator:
        condition: service_completed_successfully
    restart: unless-stopped

  workspace:
    image: dockersamples/labspace-workspace-node:v0.27.1
    depends_on:
      configurator:
        condition: service_completed_successfully
      socket-proxy:
        condition: service_started
    ports:
      - 8085:8085 # For the IDE itself
      - 3000:3000 # For the Node application running in the IDE
    volumes:
      - socket-proxy:/var/run
      - labspace-content:/home/coder/project
      - docker-creds:/docker-creds
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/public-key
        volume:
          subpath: public-key
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/socket
        volume:
          subpath: socket


  host-republisher:
    image: dockersamples/labspace-host-port-republisher:v0.27.1
    pull_policy: always
    restart: always
    volumes:
      - socket-proxy:/var/run
    network_mode: service:workspace
    environment:
      LABEL_FILTER: labspace-resource=true
    depends_on:
      - workspace
      - socket-proxy

  workspace-cleaner:
    image: dockersamples/labspace-cleaner:v0.27.1
    pull_policy: always
    volumes:
      - socket-proxy:/var/run
    environment:
      LABEL_FILTER: labspace-resource=true
    depends_on:
      - socket-proxy

  socket-proxy:
    image: mikesir87/docker-socket-proxy:v1.3.1
    volumes:
      - socket-proxy:/tmp/proxy
      - /var/run/docker.sock:/var/run/docker.sock
    configs:
      - source: docker-proxy-config-labels
        target: /etc/docker-socket-proxy/config.d/add-and-require-labels.yaml
      - source: docker-proxy-mount-remap
        target: /etc/docker-socket-proxy/config.d/mount-path-remap.yaml
      - source: docker-proxy-mount-allowlist
        target: /etc/docker-socket-proxy/config.d/mount-path-allowlist.yaml
      - source: docker-proxy-forward-proxied-socket
        target: /etc/docker-socket-proxy/config.d/forward-proxied-socket.yaml
      - source: docker-proxy-add-to-labspace-network
        target: /etc/docker-socket-proxy/config.d/add-to-labspace-network.yaml
    environment:
      DEBUG_LOGS: "true"
      LISTEN_SOCKET_PATH: /tmp/proxy/docker.sock

volumes:
  socket-proxy:
    name: labspace-socket-proxy
  labspace-content:
    name: labspace-content
  labspace-instructions:
  docker-creds:
  labspace-support:

networks:
  default:
    name: labspace

configs:
  docker-proxy-config-labels:
    content: |
      mutators:
        # Add labels to all newly created objects
        - type: addLabels
          labels:
            labspace-resource: "true"
      responseFilters:
        # Only return objects with the labels we mutated on
        - type: labelFilter
          objectsToFilter:
            - containers
            - volumes
            - networks
          requiredLabels:
            labspace-resource: "true"
  docker-proxy-mount-remap:
    content: |
      # Remap the project directory to the labspace content volume, since the project
      # is running out of a volume.
      mutators:
        - type: mountPath
          from: /home/coder/project
          to: labspace-content
  docker-proxy-mount-allowlist:
    content: |
      gates:
        - type: mountSource
          allowedSources:
            - labspace-content
            - labspace-socket-proxy
            - buildx_buildkit_default_state
            - label:labspace-resource=true
  docker-proxy-forward-proxied-socket:
    content: |
      # If requests to use the Docker Socket are used (such as Testcontainers),
      # use the proxied one to ensure permissions, remappings, etc. are applied
      mutators:
        - type: mountPath
          from: /var/run/docker.sock
          to: labspace-socket-proxy/docker.sock
  docker-proxy-add-to-labspace-network:
    content: |
      mutators:
        - type: addToNetwork
          networks:
            - labspace

---
#########################################################################
# This Compose file extends the default Labspace setup by adding the following elements:
#
# - A Traefik reverse proxy to route traffic to Gitea and k3s
# - A Gitea server for Git hosting and CI/CD
# - A Gitea self-hosted runner for executing CI jobs
# - A k3s Kubernetes cluster
#
# This provides a "SDLC-in-a-box" environment for end-to-end labs.
#
# Additional documentation is found in the docs/sdlc-labspace.md file.
#########################################################################

services:
  ############################################################
  # Overrides to Labspace default services
  ############################################################
  workspace:
    environment:
      SKIP_CI_SECRET_SETUP: ${SKIP_CI_SECRET_SETUP:-false}
    volumes:
      - gitea-outputs:/gitea-outputs:ro
      - k3s-output:/kubeconfig:ro
      - type: volume
        source: labspace-support
        target: /etc/labspace-support/k8s-setup
        volume:
          subpath: k8s-setup
    configs:
      - source: workspace-entrypoint-script
        target: /entrypoint.d/50-entrypoint-script.sh
        mode: 0755
      - source: workspace-wait-for-bootstrap
        target: /startup-scripts/00-wait-for-bootstrap.sh
        mode: 0755
      - source: workspace-ci-secrets
        target: /startup-scripts/10-ci-secrets.sh
        mode: 0755
      - source: workspace-kubeconfig-setup
        target: /startup-scripts/10-kubeconfig-setup.sh
        mode: 0755
      - source: workspace-git-setup
        target: /startup-scripts/20-git-setup.sh
        mode: 0755
      - source: workspace-configure-k3s-coredns
        target: /startup-scripts/15-configure-k3s-coredns.sh
        mode: 0755
    depends_on:
      git:
        condition: service_healthy
      k3s:
        condition: service_healthy
      zone-file-creator:
        condition: service_completed_successfully

  interface:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.interface.rule=Host(`dockerlabs.xyz`)"
      - "traefik.http.routers.interface.entrypoints=web"
      - "traefik.http.routers.interface.service=interface"
      - "traefik.http.services.interface.loadbalancer.server.port=3030"

  ############################################################
  # New services
  ############################################################
  
  cert-creator:
    image: alpine
    command: /generate-certs.sh
    configs:
      - source: generate-traefik-certs
        target: /generate-certs.sh
        mode: 0755
    volumes:
      - labspace-traefik-certs:/certs

  zone-file-creator:
    image: dhi.io/wbitt-network-multitool:3-alpine3.23-dev
    command: /create-zone-file.sh
    configs:
      - source: create-zone-file
        target: /create-zone-file.sh
        mode: 0755
    volumes:
      - labspace-support:/labspace-support
    depends_on:
      traefik:
        condition: service_started 

  traefik:
    image: traefik:3.6.13
    command:
      - --api.insecure=true
      - --providers.docker=true
      - --providers.docker.exposedbydefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.ssh.address=:22
      - --entrypoints.websecure.address=:443
      - --entrypoints.websecure.http.tls=true
      - --providers.file.filename=/etc/traefik/cert-config.yaml
    ports:
      - "22:22"
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - labspace-traefik-certs:/certs:ro
    configs:
      - source: traefik-cert-config
        target: /etc/traefik/cert-config.yaml
    networks:
      default:
        aliases:
          - git.dockerlabs.xyz
          - registry.dockerlabs.xyz
          - app.dockerlabs.xyz
    depends_on:
      cert-creator:
        condition: service_completed_successfully

  registry:
    image: ghcr.io/project-zot/zot:v2.1.15
    labels:
      traefik.enable: true
      traefik.http.routers.registry.rule: Host(`registry.dockerlabs.xyz`)
      traefik.http.services.registry.loadbalancer.server.port: "5000"
      traefik.http.middlewares.remove-frame-headers.headers.customresponseheaders.X-Frame-Options: ""
      traefik.http.routers.registry.middlewares: remove-frame-headers

  git:
    image: gitea/gitea:1.25.5
    volumes:
      - gitea-data:/data
      - gitea-outputs:/gitea-outputs
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    configs:
      - source: gitea-bootstrap
        target: /etc/gitea/bootstrap.sh
        mode: 0755
    environment:
      GITEA__server__ROOT_URL: http://git.dockerlabs.xyz/
      GITEA__server__SSH_DOMAIN: git.dockerlabs.xyz
      GITEA__server__SSH_LISTEN_PORT: 22
      GITEA__database__DB_TYPE: sqlite3

      # This prevents the first-time install page from showing (setup is done via script/env vars)
      GITEA__security__INSTALL_LOCK: "true"
    labels:
      traefik.enable: "true"
      traefik.http.routers.git.rule: Host(`git.dockerlabs.xyz`)
      traefik.http.routers.git.entrypoints: web
      traefik.http.routers.git.service: git
      traefik.http.services.git.loadbalancer.server.port: "3000"
      traefik.http.middlewares.remove-frame-headers.headers.customresponseheaders.X-Frame-Options: ""
      traefik.http.routers.git.middlewares: remove-frame-headers
      traefik.http.routers.git-secure.rule: Host(`git.dockerlabs.xyz`)
      traefik.http.routers.git-secure.entrypoints: websecure
      traefik.http.routers.git-secure.service: git
      traefik.tcp.routers.git-ssh.rule: HostSNI(`*`)
      traefik.tcp.routers.git-ssh.entrypoints: ssh
      traefik.tcp.routers.git-ssh.service: git-ssh
      traefik.tcp.services.git-ssh.loadbalancer.server.port: "22"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/healthz"]
      interval: 10s
      timeout: 10s
      retries: 3
      start_period: 5s
    post_start:
      - command: bash /etc/gitea/bootstrap.sh
        user: root

  gitea-runner:
    image: gitea/act_runner:0.3.1
    entrypoint: []
    command: bash -c "GITEA_RUNNER_REGISTRATION_TOKEN=$(cat /gitea-outputs/runner_token.txt) /sbin/tini -- run.sh"
    volumes:
      - type: volume
        source: socket-proxy
        target: /var/run/docker.sock
        volume:
          subpath: docker.sock
      - gitea-outputs:/gitea-outputs
    environment:
      GITEA_INSTANCE_URL: http://git.dockerlabs.xyz
      GITEA_RUNNER_NAME: labspace-runner

      # The runner uses 1.41 Docker API by default, but we need 1.45+ when using the socket proxy
      # due to volume rewrites using volume subpaths
      DOCKER_API_VERSION: "1.45"
    depends_on:
      git:
        condition: service_healthy

  k3s:
    # When updating the k8s version, ensure the kubectl version in the base workspace image is updated
    image: rancher/k3s:v1.35.3-k3s1
    command: server --tls-san k8s.dockerlabs.xyz
    privileged: true
    volumes:
      - k3s-server:/var/lib/rancher/k3s
      - k3s-output:/output
    environment:
      - K3S_TOKEN=labspace-k3s-token
      - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
      - K3S_KUBECONFIG_MODE=666
    tmpfs:
      - /run
      - /var/run
    ulimits:
      nproc: 65535
      nofile:
        soft: 65535
        hard: 65535
    networks:
      default:
        aliases:
          - k8s.dockerlabs.xyz
    configs:
      - source: k3s-registry-config
        target: /etc/rancher/k3s/registries.yaml
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.prod-app.rule=Host(`app.dockerlabs.xyz`)"
      - "traefik.http.routers.prod-app.entrypoints=web"
      - "traefik.http.routers.prod-app.service=prod-app"
      - "traefik.http.services.prod-app.loadbalancer.server.port=80"
    healthcheck:
      test: ["CMD", "kubectl", "get", "--raw", "/readyz"]
      interval: 10s
      timeout: 10s
      retries: 10
      start_period: 20s


volumes:
  gitea-data:
  gitea-outputs:
  k3s-server:
  k3s-output: 
  labspace-traefik-certs:


configs:
  # Generate the certs for Traefik to use
  generate-traefik-certs:
    content: |
      #!/bin/sh

      apk add --no-cache openssl >/dev/null 2>&1 || true;
      mkdir -p /certs/ca

      if [ -f /certs/dockerlabs.crt ] && [ -f /certs/dockerlabs.key ]; then
        echo 'Certs already exist, skipping generation';
        return
      fi

      # Create CA (one-time)
      if [ ! -f /certs/ca/ca.crt ] || [ ! -f /certs/ca/ca.key ]; then
        openssl genrsa -out /certs/ca/ca.key 4096
        openssl req -x509 -new -nodes -key /certs/ca/ca.key -sha256 -days 3650 \
          -subj "/CN=dockerlabs-local-ca" \
          -out /certs/ca/ca.crt
      fi

      # Create leaf key + CSR
      openssl genrsa -out /certs/dockerlabs.key 4096
      cat > /tmp/leaf.cnf <<'CNF'
      [req]
      distinguished_name = req_distinguished_name
      req_extensions = v3_req
      prompt = no

      [req_distinguished_name]
      CN = dockerlabs.xyz

      [v3_req]
      subjectAltName = @alt_names
      keyUsage = critical, digitalSignature, keyEncipherment
      extendedKeyUsage = serverAuth

      [alt_names]
      DNS.1 = dockerlabs.xyz
      DNS.2 = *.dockerlabs.xyz
      CNF

      openssl req -new -key /certs/dockerlabs.key -out /tmp/dockerlabs.csr -config /tmp/leaf.cnf

      # Sign leaf cert with CA
      openssl x509 -req -in /tmp/dockerlabs.csr \
        -CA /certs/ca/ca.crt -CAkey /certs/ca/ca.key -CAcreateserial \
        -out /certs/dockerlabs.crt -days 825 -sha256 \
        -extfile /tmp/leaf.cnf -extensions v3_req

      # combine cert & key into a PEM (optional)
      cat /certs/dockerlabs.crt /certs/dockerlabs.key > /certs/dockerlabs.pem || true;

      echo 'Generated CA and certificates';
      find /certs -type f

  # Traefik configuration to use the generated certs
  traefik-cert-config:
    content: |
      tls:
        certificates:
          - certFile: /certs/dockerlabs.crt
            keyFile: /certs/dockerlabs.key
        stores:
          default:
            defaultCertificate:
              certFile: /certs/dockerlabs.crt
              keyFile: /certs/dockerlabs.key

  # Override the default allow list to add buildkit builders and the gitea volumes
  docker-proxy-mount-allowlist:
    content: |
      gates:
        - type: mountSource
          allowedSources:
            - labspace-content
            - labspace-socket-proxy
            - buildx_buildkit_default_state
            - buildx_buildkit_builder-*
            - label:labspace-resource=true
            - act-toolcache
            - GITEA*

  # The /entrypoint.d hook in code-server does not guarantee order of execution, as it simply uses `find` to get files in the 
  # directory. To ensure our scripts run in the correct order, we prefix them with numbers and use a custom entrypoint script 
  # to sort and execute the scripts in order.
  workspace-entrypoint-script:
    content: |
      #!/bin/sh
      set -eu

      CUSTOM_DIR="/startup-scripts"

      # Nothing to do if the directory doesn't exist
      [ -d "$$CUSTOM_DIR" ] || exit 0

      # Find executable regular files, sort them, and run one by one
      find "$$CUSTOM_DIR" -type f -executable -print \
        | sort \
        | while IFS= read -r script; do
            echo "→ Running $$script"
            "$$script"
          done

  # Waits for the Gitea bootstrap to complete before allowing other startup scripts to proceed.
  # Validates that the moby user exists and the SSH key is registered, which are the last steps
  # of the bootstrap and gate everything the workspace startup scripts depend on.
  workspace-wait-for-bootstrap:
    content: |
      #!/bin/bash

      echo "Waiting for Gitea bootstrap to complete..."

      until curl -sf -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/user > /dev/null 2>&1; do
        echo "Waiting for moby user to be ready..."
        sleep 2
      done
      echo "moby user is ready."

      until curl -s -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/user/keys | grep -q '"moby-key"'; do
        echo "Waiting for SSH key to be registered..."
        sleep 2
      done
      echo "SSH key is registered. Bootstrap complete."

  # Creates all CI secrets in the Gitea repo. Can be skipped by setting SKIP_CI_SECRET_SETUP=true,
  # which is useful for labs that guide users through creating the secrets themselves.
  workspace-ci-secrets:
    content: |
      #!/bin/bash

      if [ "$$SKIP_CI_SECRET_SETUP" = "true" ]; then
        echo "SKIP_CI_SECRET_SETUP is set. Skipping CI secret creation."
        exit 0
      fi

      SECRETS_JSON=$(curl -s -u 'moby:moby1234' http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets)

      # Create Gitea registry secrets
      if echo "$$SECRETS_JSON" | grep -q '"DOCKER_USERNAME"'; then
        echo "Gitea registry secrets already exist. Skipping."
      else
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_USERNAME -H "Content-Type: application/json" -d '{"data":"moby"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_PASSWORD -H "Content-Type: application/json" -d '{"data":"moby1234"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKER_REGISTRY -H "Content-Type: application/json" -d '{"data":"registry.dockerlabs.xyz"}'
        echo "Gitea registry secrets created."
      fi

      # Create Docker Hub secrets
      if echo "$$SECRETS_JSON" | grep -q '"DOCKERHUB_USERNAME"'; then
        echo "Docker Hub secrets already exist. Skipping."
      else
        HUB_AUTH=$(jq '.auths."https://index.docker.io/v1/".auth' -r ~/.docker/config.json | base64 -d -)
        USERNAME=$(echo $$HUB_AUTH | cut -d: -f1)
        TOKEN=$(echo $$HUB_AUTH | cut -d: -f2)

        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKERHUB_USERNAME -H "Content-Type: application/json" -d '{"data":"'"$$USERNAME"'"}'
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/DOCKERHUB_PASSWORD -H "Content-Type: application/json" -d '{"data":"'"$$TOKEN"'"}'
        echo "Docker Hub secrets created."

        unset HUB_AUTH
        unset USERNAME
        unset TOKEN
      fi

      # Create KUBECONFIG secret
      if echo "$$SECRETS_JSON" | grep -q '"KUBECONFIG"'; then
        echo "KUBECONFIG secret already exists. Skipping."
      else
        KUBE_CONFIG=$(cat /kubeconfig/kubeconfig.yaml | sed 's/127.0.0.1/k8s.dockerlabs.xyz/g')
        curl -s -u 'moby:moby1234' -X PUT http://git.dockerlabs.xyz/api/v1/repos/moby/demo-app/actions/secrets/KUBECONFIG -H "Content-Type: application/json" -d '{"data":"'"$(echo -n "$$KUBE_CONFIG")"'"}'
        echo "KUBECONFIG secret created."
      fi

  workspace-git-setup:
    content: |
      #!/bin/bash

      # Configure the SSH key for git access
      sudo mkdir -p /home/coder/.ssh
      sudo cp /gitea-outputs/id_rsa /home/coder/.ssh/id_rsa 
      sudo cp /gitea-outputs/id_rsa.pub /home/coder/.ssh/id_rsa.pub
      sudo chown -R coder:coder /home/coder/.ssh

      # Setup git config
      ssh-keyscan -H git.dockerlabs.xyz >> /home/coder/.ssh/known_hosts
      git config --global init.defaultBranch main
      git config --global user.name 'Moby'
      git config --global user.email 'moby@local'
      
      # Commit the initial Labspace project to the git repo
      cd /home/coder/project

      # Ensure .gitignore exists
      touch .gitignore

      # Function to safely append to gitignore
      add_to_gitignore() {
        local entry="$$1"

        # Skip if entry already exists
        if grep -qxF "$$entry" .gitignore 2>/dev/null; then
          return
        fi

        # Ensure file ends with newline before appending (sed adds it if missing)
        sed -i -e '$$a\' .gitignore 2>/dev/null || true

        # Now append the entry
        echo "$$entry" >> .gitignore
      }

      rm -rf .git
      git init
      git remote add origin [email protected]:moby/demo-app.git
      git add -A
      git commit -m 'Initial commit'
      git push -u origin main

  gitea-bootstrap:
    content: |
      #!/bin/bash

      echo "Running"
      while ! curl -sL http://localhost:3000/user/login > /dev/null; do
        sleep 2
      done
      echo "Gitea is up - proceeding with bootstrap"

      # Create the 'moby' user if it doesn't exist
      users=$(su-exec git /usr/local/bin/gitea admin user list)
      if echo "$$users" | grep -q '^moby '; then
          echo "User 'moby' already exists. Skipping account creation."
      else
          su-exec git /usr/local/bin/gitea admin user create --username moby --password 'moby1234' --email admin@local --admin --must-change-password=false

          while ! curl -sL -u 'moby:moby1234' http://localhost:3000/api/v1/user > /dev/null; do
            echo "Waiting for user to be fully ready to use..."
            sleep 2
          done          
      fi
      
      # Check if demo-app repo exists. Create it if it doesn't.
      repo_status=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/v1/repos/moby/demo-app)
      if [ "$$repo_status" = "404" ]; then
          echo "Repository 'moby/demo-app' not found. Creating it..."
          curl -s -H "Content-type: application/json" -u 'moby:moby1234' -d '{"name":"demo-app","private":false}' http://localhost:3000/api/v1/user/repos
          echo "Repository 'moby/demo-app' created."
      else
          echo "Repository 'moby/demo-app' already exists. Skipping creation."
      fi

      # Get and store registration token for the runner
      TOKEN_JSON=$(curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/repos/moby/demo-app/actions/runners/registration-token)
      REGISTRATION_TOKEN=$(echo $$TOKEN_JSON | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
      echo -n $$REGISTRATION_TOKEN > /gitea-outputs/runner_token.txt

      # Create SSH key for user 'moby' if it doesn't exist
      if [ ! -f /gitea-outputs/id_rsa ]; then
          ssh-keygen -t rsa -b 4096 -N "" -f /gitea-outputs/id_rsa
          echo "SSH key generated for user 'moby'."
      fi

      # Add the public key to the user's SSH keys via Gitea API if not already added
      keys=$(curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/user/keys)
      if echo "$$keys" | grep -q "moby-key"; then
          echo "SSH key already added to user 'moby'. Skipping key addition."
      else
        pub_key=$(cat /gitea-outputs/id_rsa.pub)
        curl -s -u 'moby:moby1234' http://localhost:3000/api/v1/user/keys -H "Content-Type: application/json" -d "{\"title\":\"moby-key\",\"key\":\"$$pub_key\"}"
        echo "SSH key added to user 'moby'."
      fi

  # Configure k3s with credentials and TLS settings for the Gitea registry
  k3s-registry-config:
    content: |
      configs:
        "registry.dockerlabs.xyz":
          auth:
            username: moby
            password: moby1234
          tls:
            insecure_skip_verify: true

  workspace-kubeconfig-setup:
    content: |
      #!/bin/bash

      set -e

      # Setup the kubeconfig for the coder user
      mkdir -p ~/.kube
      cat /kubeconfig/kubeconfig.yaml | sed 's/127.0.0.1/k8s.dockerlabs.xyz/g' > ~/.kube/config

  create-zone-file:
    content: |
      #!/bin/sh

      set -e

      IP=$$(dig +short git.dockerlabs.xyz A | grep -E '^[0-9.]+$$' | head -n 1)

      mkdir -p /labspace-support/k8s-setup
      cat << EOF > /labspace-support/k8s-setup/zone-file-config-map.yaml
      apiVersion: v1
      kind: ConfigMap
      metadata:
        name: coredns-custom
        namespace: kube-system
      data:
        dockerlabs-xyz.server: |
          dockerlabs.xyz:53 {
            log
            errors
            file /etc/coredns/custom/dockerlabs.xyz
          }
        dockerlabs.xyz: |
          \$$ORIGIN dockerlabs.xyz.
          \$$TTL 3600

          @   IN  SOA ns1.dockerlabs.xyz. hostmaster.dockerlabs.xyz. (
                  2026020601 ; serial (YYYYMMDDnn)
                  3600       ; refresh
                  900        ; retry
                  1209600    ; expire
                  3600       ; minimum
          )

              IN  NS  ns1.dockerlabs.xyz.

          ; A records
          @   IN  A   $$IP
          *   IN  A   $$IP
      EOF

  workspace-configure-k3s-coredns:
    content: |
      #!/bin/bash

      kubectl apply -f /etc/labspace-support/k8s-setup/zone-file-config-map.yaml
      if kubectl -n kube-system get deployment coredns > /dev/null 2>&1; then
        kubectl -n kube-system rollout restart deployment/coredns
      fiservices:
  configurator:
    environment:
      PROJECT_CLONE_URL: https://github.com/dockersamples/labspace-containerized-sdlc

  workspace:
    ports: !override
      - 8085:8085configs:
  project_source_tar:
    content: |
      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

services:
  configurator:
    configs:
      - source: project_source_tar
        target: /etc/labspace-support/content.tar.base64
    environment:
      PROJECT_TAR_PATH: /etc/labspace-support/content.tar.base64

Docker commands

docker compose -f oci://dockersamples/labspace-containerized-sdlc:latest up

Use the above command to pull and run the Compose file. Learn more.

Images used

Image

A minimal Docker image based on Alpine Linux with a complete package index and only 5 MB in size!


Pulls

1B+

Stars

11554

Last Updated

2 days

Image + 1 more

Traefik, The Cloud Native Edge Router


Pulls

1B+

Stars

3664

Last Updated

3 days

Image


Pulls

50M+

Stars

70

Last Updated

6 days

Image

Gitea: Git with a cup of tea - A painless self-hosted Git service.


Pulls

100M+

Stars

1057

Last Updated

1 day

Image

Deprecated Gitea runner. Moved to gitea/runner


Pulls

5M+

Stars

34

Last Updated

5 months

Image


Pulls

10K+

Stars

0

Last Updated

3 months

Image


Pulls

10K+

Stars

0

Last Updated

3 months

Image


Pulls

10K+

Stars

0

Last Updated

3 months

Image


Pulls

10K+

Stars

0

Last Updated

3 months

Image


Pulls

10K+

Stars

0

Last Updated

3 months

Image


Pulls

10K+

Stars

0

Last Updated

8 months