Contents

FAQ: secure operations with Univio Airlock

Contents

Secure container operation within Univio Airlock

This FAQ explains how to configure and operate devcontainer-based workspaces in Airlock.

FAQ

What is the minimum .devcontainer/devcontainer.json configuration Airlock expects?

Airlock auto-detection only needs the file to exist at .devcontainer/devcontainer.json. For practical use, include at least name and either image or build.

{
  "name": "Airlock Dev",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04"
}

Should I place .devcontainer/devcontainer.json at the repository root or inside a subfolder?

Use the repository root. Airlock currently checks only this path:

.devcontainer/devcontainer.json

If your real setup is in subfolders, keep a root config that points to your target folder.

{
  "name": "Airlock Monorepo",
  "workspaceFolder": "/workspace/airlock/services/tunnel-client",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22"
}

How does Univio Airlock detect whether my repository supports devcontainers?

On workspace init, Airlock checks the repo for .devcontainer/devcontainer.json and tries refs in this order: HEAD, main, master.

When does Univio Airlock enable the containers capability for a workspace?

When Univio Airlock detects a devcontainer file, Airlock requests capabilities: ["containers"] on create, and also tries to patch existing workspaces.

{
  "gitUrl": "https://git.example.com/team/repo.git",
  "capabilities": ["containers"]
}

Can I force-enable or disable the containers capability manually?

Yes. Use workspace settings in UI or patch via API.

curl -X PATCH "https://airlock.example.com/api/workspaces/<workspace-id>" \
  -H "Authorization: Bearer <id-token>" \
  -H "Content-Type: application/json" \
  -d '{"capabilities":["containers"]}'

Disable:

curl -X PATCH "https://airlock.example.com/api/workspaces/<workspace-id>" \
  -H "Authorization: Bearer <id-token>" \
  -H "Content-Type: application/json" \
  -d '{"capabilities":[]}'

What happens if my repository does not have a .devcontainer/devcontainer.json file?

The workspace is created without containers capability and runs in the standard workspace mode.

{
  "gitUrl": "https://git.example.com/team/repo.git"
}

Which base image should I choose for a devcontainer in Airlock?

Start from official devcontainer images and pin versions. Choose by stack, not by distro preference.

{
  "name": "Node 22",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22"
}

For polyglot projects:

{
  "name": "Polyglot",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04"
}

How do I install Node.js, Python, Java, or other tools inside the devcontainer?

Prefer devcontainer features for standard toolchains.

{
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "22" },
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/java:1": { "version": "21" }
  }
}

How do I persist tools and dependencies across workspace restarts?

Persist through declarative config (image, Dockerfile, features), not manual setup. Keep project dependencies in repo-managed lockfiles.

{
  "name": "Airlock Dev",
  "build": {
    "dockerfile": "Dockerfile"
  },
  "postCreateCommand": "npm ci"
}
FROM mcr.microsoft.com/devcontainers/typescript-node:22
RUN npm i -g pnpm@10.6.0

Should I install project tools in the devcontainer image or in postCreateCommand?

Rule of thumb: install stable toolchain in image; install repo dependencies in postCreateCommand.

{
  "postCreateCommand": "npm ci --no-audit --no-fund",
  "postStartCommand": "git config --get user.email >/dev/null || true"
}

How do I make sure everyone on the team uses the same tool versions?

Pin everything that affects builds and tests.

{
  "image": "mcr.microsoft.com/devcontainers/typescript-node:22",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "22.14.0" }
  }
}
{
  "engines": {
    "node": "22.x"
  }
}

How do I configure postCreateCommand and postStartCommand for reliable setup?

Keep commands idempotent and fail fast.

{
  "postCreateCommand": "bash .devcontainer/post-create.sh",
  "postStartCommand": "bash .devcontainer/post-start.sh"
}
#!/usr/bin/env bash
set -euo pipefail
npm ci --no-audit --no-fund

How do I keep devcontainer startup fast for large repositories?

Move heavy installs to image build, keep startup hooks small, and avoid rebuilding unchanged layers.

FROM mcr.microsoft.com/devcontainers/typescript-node:22
RUN apt-get update && apt-get install -y --no-install-recommends ripgrep jq && rm -rf /var/lib/apt/lists/*
{
  "postCreateCommand": "npm ci --prefer-offline"
}

How do I debug a failing devcontainer up in Airlock?

Run devcontainer up manually in the workspace and inspect output.

cd /workspace/<repo>
devcontainer up --workspace-folder /workspace/<repo> --log-level trace

If workspace fails to start, then disable container support first to force a standard workspace to spawn.

How do I run tests in Airlock?

Use Docker wrapper commands from your repo root or run through the devcontainer.

docker run --rm \
  -v "$PWD/services/tunnel-client:/app" \
  -v "airlock-tunnel-client-node-modules:/app/node_modules" \
  -w /app node:24-alpine \
  sh -lc "npm ci --no-audit --no-fund && npm run test"

Or, when your devcontainer is up:

devcontainer exec --workspace-folder /workspace/airlock -- npm run --prefix services/tunnel-client test

Should package.json scripts stay node-native, or should they call Docker commands?

Keep package.json scripts node-native. Put Docker wrappers in Makefile or scripts/.

{
  "scripts": {
    "test": "tsx --test src/**/*.test.ts",
    "build": "tsc"
  }
}
tunnel-client-test:
	docker run --rm -v "$(PWD)/services/tunnel-client:/app" -w /app node:24-alpine sh -lc "npm ci && npm run test"

How should I structure Makefile targets for running service tests in Airlock?

Use a generic pattern in repository root and pass service name.

NODE_IMAGE ?= node:24-alpine

svc-ci:
	@test -n "$(SERVICE)"
	docker run --rm \
	  -v "$(PWD)/services/$(SERVICE):/app" \
	  -v "airlock-$(SERVICE)-node-modules:/app/node_modules" \
	  -w /app $(NODE_IMAGE) \
	  sh -lc "npm ci --no-audit --no-fund && npm run test && npm run build"

Usage:

make svc-ci SERVICE=tunnel-client

How do I expose application ports from the devcontainer to code-server and browser access?

For app preview ports, add platform-level routing explicitly.

{
  "forwardPorts": [3000, 5173],
  "portsAttributes": {
    "3000": { "label": "API" },
    "5173": { "label": "Frontend" }
  }
}

Can I enforce a fail-closed policy so workspaces do not run outside devcontainer mode?

Yes. You can configure it at environment level among other compliance requirements.

How do I use Docker-in-Docker safely inside an Airlock devcontainer workspace?

Prefer one daemon and least privilege. In Airlock, core already runs Docker daemon; avoid nested daemons unless required.

{
  "mounts": [
    "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
  ]
}

Validate access:

docker info
docker run --rm hello-world

How do I use Docker Compose from inside the devcontainer?

Install Docker CLI with Compose plugin and use normal Compose workflow.

docker compose version
docker compose -f docker-compose.yml up -d
docker compose ps

How do I securely pass secrets and environment variables into the devcontainer?

Store secrets in Airlock variables, inject them into workspace env, then map them into devcontainer env variables.

{
  "containerEnv": {
    "NPM_TOKEN": "${localEnv:NPM_TOKEN}",
    "PIP_INDEX_URL": "${localEnv:PIP_INDEX_URL}"
  }
}

Never commit secrets to .devcontainer/devcontainer.json or Git.

How do I configure access to private package registries inside the devcontainer?

Reference tokens through environment variables and repo-local config files.

# .npmrc
@your-scope:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
pip config set global.index-url "$PIP_INDEX_URL"

How do I update devcontainer configuration without breaking existing workspaces?

Use additive changes, pin versions, and roll out in small steps.

git checkout -b feat/devcontainer-update
# edit .devcontainer/devcontainer.json
git commit -m "devcontainer: add node 22 feature"

Test with a fresh workspace before merge.

How do I roll back to a previous devcontainer setup if a change causes issues?

Revert .devcontainer changes and recreate workspace.

git log -- .devcontainer
git revert <bad-commit-sha>

Then create or restart workspace to apply previous config.

How should we connect local developer workflow, Airlock workflow, and GitLab CI so they stay consistent?

Keep one command contract:

  1. npm scripts are canonical (test, build).
  2. Airlock convenience wrappers call those scripts via Docker/devcontainer.
  3. GitLab CI runs the same scripts in test stage before build.
stages:
  - test
  - build

test_tunnel_client:
  stage: test
  image: node:24-alpine
  script:
    - cd services/tunnel-client
    - npm ci --no-audit --no-fund
    - npm run test
    - npm run build