Quickstart¶
This quickstart uses one scenario: creating a container image that prints a required message. You will package that scenario either as a scored SkillCred item or as a guided lab.
What you will build
A sandbox around one container-image task:
| Path | Outcome |
|---|---|
| SkillCred | Score whether a candidate can create linux-foundation-education/hello:1.0. |
| Lab | Guide a learner through creating linux-foundation-education/hello:1.0. |
The image must print:
hello tux
Before you begin, set up your Codespace.
Follow your content repository workflow¶
Step 1: Scaffold the environment
Run the initializer from the items/ directory:
cd items
sandbox init
When prompted: select Exam task → name it hello-image → accept all other defaults → Create → validate.
Enter the item directory (named after what you entered above):
cd hello-image
You now have a bootable sandbox with one Debian-based virtual machine. Drop into the VM to see what the candidate will see:
sandbox shell
You can now exit the virtual machine by using Ctrl+D
You have successfully used sandbox init to scaffold out a sandbox environment, started the virtual machine, and stopped it.
You may want to take some time to review the file structure created in step 1.
Generated file structure
hello-image/
├── .gitignore
├── metadata.yaml
├── sandbox.yaml
├── task.en.md
└── host1/
├── assets/
│ └── .gitkeep
└── scripts/
├── build.sh
├── setup.sh
├── answer.sh
└── score.sh
Checkpoint
You have a scaffolded SkillCred item in items/hello-image.
Step 2: Write the candidate task
task.en.md is what the candidate reads. The sandbox renders it as a MkDocs site in the instructions tool, visible by default in the left panel when the instructions tool is defined in sandbox.yaml.
You may preview the instructions as they will be rendered by the instructions tool in your development environment using the sandbox CLI. It's helpful to keep this running in a Codespace terminal while you author because the preview auto-updates.
sandbox instructions
Preview server started
The port may differ in your environment. Open the localhost URL shown in your terminal.
ghcr.io/lf-certification/p3-sandbox-instructions-generator:latest
Building instructions...
Starting preview server at http://127.0.0.1:<port>
The preview tab shows the instructions exactly as the platform will render them to the end user of your environment.
Keep preview running
Leave sandbox instructions running while you edit. The preview reloads automatically when task.en.md changes.
Open task.en.md in your editor and save the contents to the following:
Copy into task.en.md
# Task
A starter `Dockerfile` is available at:
```text
/home/tux/container-image/Dockerfile
```
Modify it, then create a container image tagged:
```text
linux-foundation-education/hello:1.0
```
When the image is run, it must print exactly:
```text
hello tux
```
You may use `docker build`.
Checkpoint
The preview tab now shows the candidate-facing task.
You may now Ctrl+C in the terminal where sandbox instructions is running and close the preview tab.
Step 3: Implement the sandbox lifecycle scripts
Open sandbox.yaml and configure the tools. Each tool name becomes a tab the candidate sees. Then implement the lifecycle scripts in host1/scripts/.
Lifecycle map for this task
| Script | When it runs | Purpose in this task |
|---|---|---|
build.sh |
Once, when the VM image is built | Install Docker, enable and start it with --now, and copy a starter asset into the candidate workspace. |
setup.sh |
Every time a sandbox instance starts | No-op; Docker enabled with --now in build.sh starts reliably with the VM. |
answer.sh |
On demand during local testing | Apply the reference solution by creating the Dockerfile and building the expected image. |
score.sh |
On demand during grading | Run independent checks for image existence and expected container output. |
First, add a starter Dockerfile asset that candidates can modify:
Copy into host1/assets/Dockerfile.starter
FROM docker.io/library/alpine:3.20
# TODO: make this image print the required message.
CMD ["echo", "replace me"]
Then update host1/scripts/build.sh to install Docker, enable it at boot, and copy the starter asset into the candidate workspace:
Copy into host1/scripts/build.sh
#!/usr/bin/env bashp
install::docker
systemctl enable --now docker
mkdir -p /home/tux/container-image
chown -R tux:tux /home/tux/container-image
file::copy assets/Dockerfile.starter /home/tux/container-image/Dockerfile
usermod -aG docker tux
In host1/scripts/setup.sh, leave it as a no-op:
No wait needed for Docker
Docker was enabled with systemctl enable --now in build.sh, so it starts reliably with the VM. Only add waits in setup.sh when startup delay is significant enough to outlast SSH availability — Kubernetes cluster readiness is the canonical case.
Copy into host1/scripts/setup.sh
#!/usr/bin/env bashp
In host1/scripts/answer.sh, write the reference solution:
Copy into host1/scripts/answer.sh
#!/usr/bin/env bashp
mkdir -p /home/tux/container-image
chown -R tux:tux /home/tux/container-image
cat > /home/tux/container-image/Dockerfile <<'EOF'
FROM docker.io/library/alpine:3.20
CMD ["echo", "hello tux"]
EOF
chown tux:tux /home/tux/container-image/Dockerfile
sudo -iu tux docker build -t linux-foundation-education/hello:1.0 /home/tux/container-image
In host1/scripts/score.sh, check that the image exists and prints the expected message:
Copy into host1/scripts/score.sh
#!/usr/bin/env bashp
IMAGE_TAG="linux-foundation-education/hello:1.0"
EXPECTED_OUTPUT="hello tux"
imageExists() {
sudo -iu tux docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1
}
imagePrintsExpectedMessage() {
output="$(sudo -iu tux docker run --rm "${IMAGE_TAG}")"
test "${output}" = "${EXPECTED_OUTPUT}"
}
scoring::check imageExists
scoring::check imagePrintsExpectedMessage
scoring::report
Checkpoint
The sandbox now has setup, reference answer, and scoring logic for the container image task.
Step 4: Add item metadata
Open metadata.yaml, change the generated settings.nickname, and add the required item fields:
Update metadata.yaml
---
settings:
nickname: [petname]
competency: quickstart
difficulty: easy
title: "Build a greeting container image"
total_check_count: 2
revision: 1
Tip
nickname should be a unique item name, such as a generated pet name. competency must match an id in blueprint.yaml. total_check_count must match the number of checks in score.sh. See Blueprint reference for the full list of metadata constraints.
Checkpoint
The metadata now matches the two scoring::check calls in score.sh.
Step 5: Validate
Rebuild the VM image, then run the lifecycle test:
sandbox build && sandbox test
Why build first?
You changed build.sh after first starting the sandbox. sandbox test runs against the existing sandbox build; it does not rebuild a pre-existing VM image for you.
sandbox test succeeds only when both scoring paths are valid:
-
No candidate work
setup.sh->score.shExpected score: 0 of 2 checks pass
Confirms the task does not award points before the candidate does any work.
-
Reference answer
setup.sh->answer.sh->score.shExpected score: 2 of 2 checks pass
Confirms the reference answer satisfies every scoring check.
Successful test result
The final report includes checks_executed: 2, checks_passed: 2, and the two successful checks: imageExists and imagePrintsExpectedMessage.
Test successful!
Exact final host report
The CLI prints the JSON after host1 | on one line. It is formatted here for readability.
{
"output_version": 1,
"checks_executed": 2,
"checks_passed": 2,
"checks_successful": [
"imageExists",
"imagePrintsExpectedMessage"
],
"checks_failed": []
}
Preview the candidate instructions:
sandbox instructions
Validation runs at commit time
A skills validate item git hook re-checks the items you touched on every git commit; you do not need to run validation manually.
You are done when
sandbox build && sandbox testends withTest successful!.- The final score report has
checks_executed: 2andchecks_passed: 2. - The instructions preview renders the candidate task correctly.
Step 1: Scaffold the lab environment
Run the lab initializer from the items/ directory:
cd items
sandbox init
When prompted: select Lab exercise → name it hello-image → accept all other defaults → Create → validate.
Enter the lab directory:
cd hello-image
Drop into the VM to see the learner environment:
sandbox shell
You can now exit the virtual machine by using Ctrl+D
Generated file structure
hello-image/
├── .gitignore
├── instructions.md
├── sandbox.yaml
└── host1/
├── assets/
│ └── .gitkeep
└── scripts/
├── build.sh
├── setup.sh
├── answer.sh
└── score.sh
Checkpoint
You have a scaffolded lab sandbox in items/hello-image.
Step 2: Register the lab
Describe the lab in metadata.yaml inside the lab directory:
Copy into items/hello-image/metadata.yaml
---
competency: quickstart
difficulty: easy
title: "Create a greeting container image"
total_check_count: 0
revision: 1
Scoring comes later
total_check_count: 0 is allowed while the lab has no scoring checks yet. Once score.sh calls scoring::check, set it to the exact check count.
Then select the lab in manifest.yaml at the content repository root:
Copy into manifest.yaml
assemblies:
primary:
selected_items:
- hello-image
Checkpoint
The lab content repository now selects items/hello-image.
Step 3: Implement the sandbox lifecycle scripts
Open sandbox.yaml and review the tools configuration. Each tool name becomes a tab the learner sees, then implement the lifecycle scripts in host1/scripts/.
Scoring is still useful in labs. It validates that the instructions being taught are achievable in the authored environment produced by the lifecycle scripts.
Lifecycle map for this lab
| Script | When it runs | Purpose in this lab |
|---|---|---|
build.sh |
Once, when the VM image is built | Copy a starter asset into the learner workspace. |
setup.sh |
Every time a sandbox instance starts | No-op, no services to check for readiness in this lab. |
answer.sh |
On demand during local testing | Apply the reference solution by running the same Docker setup commands taught to the learner, then building the expected image. |
score.sh |
On demand during validation | Check whether the learner-visible instructions produce a usable Docker runtime, the expected image, and the expected output. |
First, add a starter Dockerfile asset that learners can modify:
Copy into host1/assets/Dockerfile.starter
FROM docker.io/library/alpine:3.20
# TODO: make this image print the required message.
CMD ["echo", "replace me"]
Then update host1/scripts/build.sh to copy the starter asset into the learner workspace. In this lab, Docker setup is part of what the learner practices, so do not bake it into the VM image.
Copy into host1/scripts/build.sh
#!/usr/bin/env bashp
mkdir -p /home/tux/container-image
chown -R tux:tux /home/tux/container-image
file::copy assets/Dockerfile.starter /home/tux/container-image/Dockerfile
In host1/scripts/setup.sh, keep the script as a no-op:
Use setup.sh only for runtime readiness
setup.sh runs every time a sandbox instance starts, after the VM boots from the image produced by build.sh. Put work here only when it depends on runtime state or verifies readiness. In this lab, Docker is intentionally not ready yet because installing it is part of the learner workflow.
Copy into host1/scripts/setup.sh
#!/usr/bin/env bashp
In host1/scripts/answer.sh, automate the same outcome the lab teaches:
Copy into host1/scripts/answer.sh
#!/usr/bin/env bashp
apt-get update
apt-get install -y docker.io
systemctl enable --now docker
usermod -aG docker tux
sudo -iu tux docker ps >/dev/null
mkdir -p /home/tux/container-image
chown -R tux:tux /home/tux/container-image
cat > /home/tux/container-image/Dockerfile <<'EOF'
FROM docker.io/library/alpine:3.20
CMD ["echo", "hello tux"]
EOF
chown tux:tux /home/tux/container-image/Dockerfile
sudo -iu tux docker build -t linux-foundation-education/hello:1.0 /home/tux/container-image
In host1/scripts/score.sh, check that Docker is usable, the image exists, and the image prints the expected message:
Copy into host1/scripts/score.sh
#!/usr/bin/env bashp
IMAGE_TAG="linux-foundation-education/hello:1.0"
EXPECTED_OUTPUT="hello tux"
dockerIsUsable() {
sudo -iu tux docker ps >/dev/null
}
imageExists() {
sudo -iu tux docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1
}
imagePrintsExpectedMessage() {
output="$(sudo -iu tux docker run --rm "${IMAGE_TAG}")"
test "${output}" = "${EXPECTED_OUTPUT}"
}
scoring::check dockerIsUsable
scoring::check imageExists
scoring::check imagePrintsExpectedMessage
scoring::report
Then set total_check_count in metadata.yaml to match the three scoring::check calls:
Update metadata.yaml
---
competency: quickstart
difficulty: easy
title: "Create a greeting container image"
total_check_count: 3
revision: 1
Checkpoint
The lab sandbox now has a prepared workspace, a reference answer, and validation logic for the guided Docker exercise.
Step 4: Write the lab instructions
instructions.md is what the learner reads. The sandbox renders it as a MkDocs site in the instructions tool, always visible in the left panel.
When you write labs for an existing catalog, the Lab instruction style reference describes the voice, heading shape, prompt titles, and copy-target conventions the catalog labs follow. The quickstart example below is a minimal starting point; production labs use more of those conventions.
Open instructions.md and write the guided steps:
Copy into instructions.md
# Lab
In this lab, you will install Docker, configure your user to run Docker commands, and create a container image that prints a greeting.
Install Docker and enable it:
```bash title="tux@host1:~$"
sudo apt-get update
sudo apt-get install -y docker.io
sudo systemctl enable --now docker
```
Add your user to the Docker group, then start a shell with the updated group membership:
```bash title="tux@host1:~$"
sudo usermod -aG docker tux
newgrp docker
```
Confirm Docker works without `sudo`:
```bash title="tux@host1:~$"
docker ps
```
A starter `Dockerfile` is available at:
```text
/home/tux/container-image/Dockerfile
```
Open the file and replace its contents with:
```dockerfile
FROM docker.io/library/alpine:3.20
CMD ["echo", "hello tux"]
```
Build the image with this tag:
```text
linux-foundation-education/hello:1.0
```
```bash title="tux@host1:~$"
cd /home/tux/container-image
docker build -t linux-foundation-education/hello:1.0 .
```
Run the image:
```bash title="tux@host1:~$"
docker run --rm linux-foundation-education/hello:1.0
```
!!! success "Expected Output"
```{.text .no-copy}
hello tux
```
Preview it at any time:
sandbox instructions
Keep preview running
Leave sandbox instructions running while you edit. The preview reloads automatically when instructions.md changes.
Checkpoint
The learner instructions now walk through the shared container image scenario.
Step 5: Validate
Rebuild the VM image, then run the lifecycle test:
sandbox build && sandbox test
Why build first?
You changed build.sh after first starting the sandbox. sandbox test runs against the existing sandbox build; it does not rebuild a pre-existing VM image for you.
sandbox test succeeds only when both validation paths are valid:
-
Before the lab steps
setup.sh->score.shExpected score: 0 of 3 checks pass
Confirms the checks do not pass before the learner follows the instructions.
-
Reference answer
setup.sh->answer.sh->score.shExpected score: 3 of 3 checks pass
Confirms the authored environment can complete the same outcome taught in the lab.
Successful test result
The final report includes checks_executed: 3, checks_passed: 3, and the three successful checks: dockerIsUsable, imageExists, and imagePrintsExpectedMessage.
Test successful!
Exact final host report
The CLI prints the JSON after host1 | on one line. It is formatted here for readability.
{
"output_version": 1,
"checks_executed": 3,
"checks_passed": 3,
"checks_successful": [
"dockerIsUsable",
"imageExists",
"imagePrintsExpectedMessage"
],
"checks_failed": []
}
Validation runs at commit time
A skills validate . git hook re-checks the lab content repository on every git commit; you do not need to run validation manually.
You are done when
- The instructions preview renders the guided lab correctly.
sandbox build && sandbox testends withTest successful!.- The final score report has
checks_executed: 3andchecks_passed: 3.
Key Resources¶
Configure virtual machines, tools, base images, resources, and multi-VM layouts.
Learn when build.sh, setup.sh, answer.sh, and score.sh run and how to write scoring checks.
Understand the sandbox model, configuration flow, and authoring building blocks.
Prepare your Codespace and local authoring tools before building content repositories.