Skip to content

Example environments for training and work

These scenarios describe topologies that recur in training and development projects. They are not configurations ready to be copied. The final design depends on the syllabus, the number of workstations, the permission model and the kind of data involved. Before launch we agree on toolchain versions, network rules, licences, the reset method and the procedure for handing over or deleting data.

An environment for Kubernetes training

The lab topology follows from the scope of the exercises. For work with Deployment, Service and Ingress, a shared cluster with a separate namespace for each participant is usually enough. Exercises involving the control plane, worker nodes or cluster failures require separate clusters or sets of VMs.

A namespace organises objects and sets the scope of some policies, but it is not a security boundary on its own. Participants still share the API server, the nodes, storage and cluster-scoped resources. We restrict access with RoleBinding, and the use of CPU, memory and object counts with ResourceQuota and LimitRange. NetworkPolicy works only if the CNI plugin in use supports it.

Before launch we run through the whole set of exercises from a participant account. The test covers registry access, PVC provisioning, DNS, Ingress, resource limits and Helm operations. Separately, we verify the environment reset and confirm that a participant cannot read objects in other namespaces or create cluster-scoped resources.

Kubernetes training cluster In the shared-cluster variant, the participant connects through kubectl and optionally Helm. The control plane manages the worker nodes. RBAC, quota, NetworkPolicy, Ingress and persistent volumes complete the isolation of the labs. PARTICIPANT KUBECTL / HELM CLUSTER / TRAINING CONTROL PLANE WORKER 01 PODS / MANY NS WORKER 02 PODS / MANY NS POLICY / ISOLATION RBAC + QUOTA NETWORKPOLICY INGRESS + PVC

Configuration

Each participant receives their own namespace, ServiceAccount, RoleBinding and kubeconfig. ResourceQuota and LimitRange enforce the agreed requests and limits. The Kubernetes, kubectl and Helm versions match the course materials, and images are pulled in advance from the appropriate registry. We enable StorageClass, IngressClass and NetworkPolicy only when they appear in the scenario.

Technical scope

The scope may cover Deployment, StatefulSet, ConfigMap, Secret, probes, Service, Ingress and PVC. Participants diagnose a rollout through Events, logs and the state of Pod and EndpointSlice objects. In the Helm part they analyse the rendered manifest, override values and roll back a specific release.

Outcome and limitations

A shared baseline removes differences in API versions and client configuration. RBAC, quota and NetworkPolicy limit how participants affect one another, but they do not provide isolation equivalent to a separate cluster. Tasks that require cluster-admin are carried out only in a dedicated environment.

Virtual machines for Docker training

Access to the Docker Engine socket lets you control a daemon running with root privileges. Membership of the docker group should therefore be treated as administrative rights over the host. A dedicated VM separates this level of access from the company endpoint and allows exercises with networks, volumes and capabilities.

The VM standardises the elements that a Dockerfile does not describe: the kernel, cgroups, the storage driver, DNS and the CPU architecture. Differences in these layers show up with bind mounts, memory limits, native dependencies and multi-architecture images. Matching Docker Engine versions alone does not guarantee an identical exercise result.

Before the start we measure the size of the images and the build context, check ports, the required registry access and the spare space for image layers, volumes and logs. We also agree on the reset boundary. If the snapshot covers the whole VM disk, code and artefacts that need to be kept must first go to a repository or external storage.

Docker environment layers Application and database containers are built from a base image and run on a VM with Docker Engine and Compose. The environment also includes a registry cache, a persistent volume and healthchecks. IMAGE BUILD / RUN CONTAINER APP CONTAINER DB VM / DOCKER ENGINE + COMPOSE RUNTIME CONTROLS REGISTRY CACHE VOLUME / DATA HEALTHCHECK

Configuration

The Linux VM has pinned versions of Docker Engine and the Compose plugin, with BuildKit enabled. The repository contains the Dockerfile, compose.yaml, healthchecks and non-production seed data. Before the training we run a build without cache, a build with a warm cache and a full start of the stack. For larger images we use a local registry cache.

Technical scope

The exercises cover multi-stage builds, caching, build arguments, image tagging and non-root processes. In Compose, participants work with service discovery, named volumes, bind mounts, healthchecks and restart policies. Diagnostics use docker inspect, docker logs, docker stats and the state of networks and volumes.

Outcome and limitations

Each participant can rebuild images, change networks and delete volumes without affecting the other workstations. A snapshot reset restores the whole VM disk, not individual Docker resources. We use rootless mode only when its limitations do not conflict with the training material.

Workstations for Android training

A repeatable Android build requires compatible versions of the JDK, Android Gradle Plugin, Gradle Wrapper, SDK Platform and Build Tools. We pin them before the training and check them on a reference project. Updating Android Studio or the SDK on the day of the class is not part of the process.

The hardest part of a remote workstation is the Android Emulator. VM acceleration requires access to virtualisation extensions and a suitable hypervisor. In an environment that itself runs inside another VM, we do not assume it is available. We enable the emulator only after confirming that /dev/kvm works and measuring the remote desktop latency.

If acceleration is not available, the VM still handles code editing, dependency resolution, builds and JVM tests. In that case we run the APK on an agreed external device. Before the class we do a clean build without the instructor's cache, run the tests and check that all Maven artefacts are available.

Android training workstation A single virtual machine runs Android Studio with the specified SDK version, the training project and the emulator. The process is complemented by the Gradle cache, ADB and logcat diagnostics, and either KVM acceleration or a physical device. VM / PER SEAT ANDROID STUDIO SDK / PINNED PROJECT BUILD + TESTS DEPLOY EMULATOR ANDROID BUILD + EXECUTION GRADLE CACHE ADB / LOGCAT KVM / DEVICE

Configuration

We pin the JDK, Android SDK Platform, Build Tools, Android Gradle Plugin and Gradle Wrapper. The project passes a clean build and the tests on a participant account. We prepare the Gradle cache only after confirming that the build also works from scratch. The AVD matches the API level used in the material.

Technical scope

The scope covers build variants, dependency resolution, unit tests, manifest analysis, logcat and working with ADB. We run instrumented tests and APK deployment to an AVD only on workstations with confirmed acceleration. Without it, the artefact is built in the VM and the test runs on an external device.

Outcome and limitations

An identical toolchain ensures comparable build output and Gradle behaviour. It does not, however, guarantee emulator performance. If the infrastructure does not provide VM acceleration, we do not present the AVD as a fully fledged part of the workstation and limit the scope to builds and tests that do not need a device.

A sandbox for AI experiments

A separate VM isolates the prototype's runtime, libraries and credentials from the developer's computer. It does not, however, solve the problem of data sent to an external model. The permissible scope of the experiment follows from the data classification, the contract with the API provider and the organisation's policy.

Before the first request we establish which fields may leave the environment, where the provider stores the request and response, and what retention period applies. The notebook uses synthetic data or a previously approved dataset. Having no route to production reduces the risk of data being pulled by accident, but it does not replace control over the payload sent to the API.

Each run records the model ID, prompt version, inference parameters, evaluation dataset and validation result. With non-deterministic models we compare the distribution of results from several runs, not a single answer. Separately, we record latency, token usage, API errors and whether the structured output conforms to the schema.

AI sandbox isolated from production Python with JupyterLab uses synthetic data and restricted access to the specified API, with no routes to production. The experiment records the model used, the metrics and the evaluation dataset, and credentials come from a separate secret store. SANDBOX VM PYTHON JUPYTERLAB TEST DATA GATED ACCESS MODELS API PRODUCTION NO LINK × EXPERIMENT RECORD SECRET STORE EVAL DATASET MODEL + METRICS

Configuration

The VM contains a pinned Python environment, a dependency lockfile, JupyterLab and an approved dataset. Egress allows only the required API endpoints. Credentials never go into the repository or the notebook. The process receives them from a secret store or from a file with restricted permissions, in line with the organisation's standard.

Technical scope

The evaluation dataset contains positive cases, negative cases and edge cases. Scoring uses explicit criteria and versioned code. Besides quality, we record the model ID, prompt version, parameters, raw response, latency, token usage, error rate and the structured output validation result.

Outcome and limitations

We can reproduce the code, dataset and parameters of the experiment. This does not guarantee an identical response if the provider changes the model or the way it is served. Closing down the environment includes deleting the VM, rotating the keys and checking retention on the API provider's side.

A lab for Apache Kafka training

A small cluster is enough for exercises with the producer, consumer, partitions and offsets. Leader election, ISR and write behaviour after losing a broker require at least three brokers and deliberately chosen values for the replication factor, min.insync.replicas and acks.

Three brokers running on one VM let you stop processes and watch the leader change. This is not, however, a test of a host or availability zone failure. If the training covers infrastructure resilience, we spread the brokers and the KRaft controller quorum across separate VMs and isolated failure domains.

The configuration of the Kafka clients is part of the lab. We pin the library version, serialisation, Schema Registry and ACLs, and the synthetic stream keeps a typical key distribution and payload size. This makes it possible to show ordering within a partition, consumer lag and the effects of a rebalance without access to production topics.

Kafka topic with three partitions A producer publishes events to a topic with three partitions. A consumer group reads the messages and tracks the offset. The bottom layer shows the leader, ISR replicas, consumer lag and rebalance. PRODUCER TOPIC / EVENTS P0 P1 P2 ● OFFSET READ CONSUMER GROUP CLUSTER BEHAVIOUR BROKER 1 / LEADER BROKER 2/3 / ISR LAG + REBALANCE

Configuration

For the broker-loss scenario we run three brokers, a topic with replication factor 3, min.insync.replicas=2 and a producer with acks=all. Each participant receives their own topic prefix or ACL. Monitoring shows the state of the brokers, leaders, ISR and consumer groups.

Technical scope

Participants analyse how the message key affects partitioning and ordering, manual and auto commit, auto.offset.reset, lag and consumer group rebalance. In the multi-broker variant they observe leader election, ISR changes, a write being rejected after the number of in-sync replicas drops, and broker recovery.

Outcome and limitations

The lab shows client and protocol semantics without using production data. Several brokers on one host do not reproduce a machine failure, storage characteristics or network latency between zones. We do not use results from such a topology to assess production capacity.

An environment for a MongoDB workshop

This scenario concerns MongoDB, not the general NoSQL category. The scope covers document modelling, the aggregation pipeline, update operators and indexes. Other non-relational databases have different consistency models, query languages and operational characteristics.

We choose between embedding and references based on the read and write paths, cardinality and how often data changes. The choice affects the atomicity of operations, document size and the number of requests. An entity diagram on its own is not enough to design a document model.

The seed data reproduces field selectivity, value distribution and the relationships between collections. Only then does explain("executionStats") provide material for comparing COLLSCAN, IXSCAN and the values of totalDocsExamined, totalKeysExamined and nReturned. We treat a cold cache and a warm cache as separate test conditions.

A document database in training The participant sends a query to MongoDB, which holds documents and indexes, and a script restores the initial state. A controlled seed makes it possible to compare execution stats and COLLSCAN and IXSCAN plans. PARTICIPANT QUERY / PLAN DB / MONGODB DOC / ORDERS DOC / USERS INDEXES RESET SCRIPT REPEATABLE QUERY TEST SEED / CARDINALITY EXPLAIN STATS COLLSCAN / IXSCAN

Configuration

We pin the MongoDB and mongosh versions. Each participant receives their own database with a restricted role or a separate instance. An idempotent reset script recreates the documents, schema validation and indexes. We run a replica set when the exercises cover transactions, change streams or read concern behaviour.

Technical scope

The scope covers embedding and references, schema validation, the aggregation pipeline and update operators. In execution plans we compare COLLSCAN, IXSCAN, FETCH and any SORT stage. For compound indexes we analyse the order of fields with respect to equality, sort and range, and whether a covered query is possible.

Outcome and limitations

Their own dataset and reset let participants compare plans without colliding with one another. The result depends on the size and distribution of the data, the state of the cache and the query engine version. The lab is for analysing the mechanism, not for publishing MongoDB benchmarks.

An isolated environment for Ansible training

Each participant works with their own control node and set of managed hosts. A playbook can install packages, change service configuration and use privilege escalation without access to company servers. After the exercise, the hosts return to a versioned baseline.

The result of a run depends on the SSH connection, the Python interpreter, the sudo configuration and the modules available on the given platform. A playbook written for Ubuntu may need changes on Debian or on a system that uses a different package manager. So we choose the distribution and system version to suit the material, not the other way round.

Changing firewall rules, the SSH configuration or the network stack can cut off the channel through which Ansible manages the host. Such exercises require a console independent of SSH and an automatic rebuild. Before the training we also test check mode and diff. Not all modules support them, and diff can reveal values that need protecting.

Ansible lab A control node with Ansible and an inventory runs playbooks over SSH on three hosts in a private network. Check mode, diff and a second run verify idempotence, and a rebuild restores the baseline. CONTROL NODE ANSIBLE + INVENTORY PLAYBOOK / SSH LAB NET / PRIVATE HOST 01 / LINUX HOST 02 / LINUX HOST 03 / LINUX CONTROL LOOP CHECK + DIFF RUN 2 / IDEMPOTENT REBUILD BASELINE

Configuration

The control node has a pinned version of ansible-core, and two or three managed hosts run in a private network. The configuration covers the inventory, SSH keys, host key verification, the Python interpreter and become. The base images and the rebuild procedure are versioned together with the materials.

Technical scope

The scope covers inventory groups, host_vars, group_vars, variable precedence, Jinja2 templates, handlers, roles and Ansible Vault. Participants use verbosity, check mode and diff, and analyse changed_when and failed_when. They check idempotence with a second run without changing the input data.

Outcome and limitations

The playbook runs on full Linux systems, so it exposes problems with the package manager, systemd, permissions and SSH. The private network limits the blast radius. The result still applies to a specific distribution and service versions, so a test in an environment matching the target is needed before rollout.

An environment for performance testing

The load generator runs on a different VM from the system under test and sends traffic only to the specified endpoint. This split makes it possible to observe the limits of the generator and of the application separately. We treat results from shared infrastructure as lab data, not as an automatic forecast of production performance.

A test starts with a workload model, ramp-up, warm-up and steady state phases, and stop conditions. The number of virtual users is a parameter of the model, not a goal in itself. We interpret p95 and p99 together with throughput, error rate and saturation of CPU, GC, connection pools, the database and storage.

Before the actual run we check the generator's CPU, memory, connection count and network throughput. JMeter measures protocol time, not page rendering or JavaScript execution in the browser. Assessing frontend performance requires a separate browser scenario and the appropriate metrics.

Load testing environment A JMeter or Gatling generator directs controlled load at the application under test. Monitoring correlates the ramp and steady phases with p95, error rate and the use of CPU, the garbage collector and disk. LOAD GEN JMETER / GATLING LOAD APP UNDER TEST MONITORING / RESPONSE TIME TEST PHASES + SIGNALS RAMP / STEADY P95 + ERROR RATE CPU / GC / I/O

Configuration

We pin the version of JMeter or Gatling and the matching runtime. Time on both VMs is synchronised, and the traffic limit is recorded in the test configuration. Monitoring covers the application, the connection pool, CPU, memory, disk I/O and the network. A separate preflight confirms that the generator has spare resources.

Technical scope

Participants define the workload model, ramp-up, warm-up, steady state and teardown. They analyse throughput, error rate, p50, p95, p99 and the number of active virtual users, and correlate them with CPU, GC, connection pools and storage. Each run records the test commit, dataset, parameters and execution time.

Outcome and limitations

Within the same configuration you can compare regressions and the relationships between load and saturation. Absolute throughput and latency figures do not establish production capacity unless we control the network topology, storage, the noisy neighbour effect and the parameters of the target infrastructure.

A lab for functional and API testing

Every test run must point to a specific build, for example a commit SHA or an immutable image tag, and a known data state. Participants do not work on one shared database. An operation performed by one person must not change the preconditions of a test run by another.

Defects in the demo application follow from documented requirements and leave a trace in the UI, an API response or the logs. This lets a participant form a hypothesis, collect the request, response and correlation ID, and then prepare a minimal reproduction path. They do not have to guess what the author of the task intended.

We provide isolation through a separate instance, schema or tenant. The reset restores records, accounts, roles and reference data, and running it repeatedly always leads to the same state. We test the reset mechanism itself before the training, together with the application and the set of test accounts.

Testing lab The tester checks the UI and sends HTTP requests to the API. The build ID, data seed and correlation ID form the reproduction context, which goes into the bug report together with the reproduction path. TESTER HTTP / API DEMO ENV + TEST DATA APP / UI API BUG REPORTS REPRODUCTION CONTEXT BUILD ID + SEED CORRELATION ID RESET + BUG REPORT

Configuration

The application version is identified by a commit SHA or an immutable image tag. Each participant receives their own instance, database schema or tenant with deterministic seed data. The materials include the requirements, the OpenAPI spec, accounts with different roles and instructions for finding the correlation ID in the logs.

Technical scope

The scope covers boundary values, equivalence classes and state transitions in the UI and the API. For HTTP we analyse status codes, headers, validation, authentication, authorisation, pagination and idempotency. A bug report contains the request, response, timestamp, correlation ID, expected result, actual result and a minimal reproduction path.

Outcome and limitations

A defect can be reproduced against a specific build ID and seed data without access to production. The lab does not automatically reproduce external integrations, race conditions or behaviour under load. These areas require test doubles or a separate topology.

Virtual machines for Rust training

The rustc version affects the available language features, compiler diagnostics and the MSRV of dependencies. Crates that use FFI additionally require specific system libraries and headers. The instruction ‘install Rust’ therefore does not define a complete workstation.

We record the toolchain in rust-toolchain.toml, and Cargo.lock stabilises the dependency graph. Feature flags and targets are also part of the project configuration. As a result, everyone gets the same compiler messages and works with the same dependency versions.

Before the class we build the whole workspace on a participant account with cargo build --locked, run the tests and Clippy, and check rustfmt. Separately, we verify native dependencies for FFI, OpenSSL and system bindings. We install Miri and cross-compilation support only when they actually feature in the syllabus.

Rust training workstation The project is compiled with pinned versions of rustc and Cargo and then goes through the tests. The lockfile, rustfmt, Clippy and explicit system dependencies keep the toolchain reproducible. VM / IDENTICAL SEAT PROJECT SOURCE CARGO BUILD RUSTC MESSAGES TESTS CARGO TEST REPRODUCIBLE TOOLCHAIN TOOLCHAIN + LOCK FMT + CLIPPY TARGET + NATIVE LIBS

Configuration

rustup installs the channel or the full version specified in rust-toolchain.toml, together with the required components and targets. The repository contains Cargo.lock, explicit feature flags and a list of system libraries. The build, tests, rustfmt and Clippy pass on a participant account.

Technical scope

The material covers ownership, borrowing, lifetimes, pattern matching, traits and generics in a program that grows over the course. Error handling uses Result, the ? operator and explicit error types. Verification comes from unit tests, integration tests and Clippy, and optionally Miri or concurrency tests.

Outcome and limitations

A pinned toolchain and lockfile stabilise diagnostics and dependency resolution. They are not enough if the build depends on system packages, the target architecture or external services. We record these dependencies in the VM image and in the project documentation.

A development environment for web applications

An environment for a web application must reproduce the runtime, database and supporting service versions used by the project. Having the dependencies present in the VM image is not enough. The repository needs a repeatable bootstrap, explicit configuration and a procedure for keeping changes before the machine expires.

The full stack may include a frontend, API, database, cache, message broker, object storage, mail catcher and background workers. For each dependency we specify a local service or a particular sandbox endpoint. As a result, starting the project does not depend on commands passed on by word of mouth between team members.

We test the bootstrap from a clean checkout: installing dependencies, migrations, seed, starting the services and healthchecks. Production secrets never go into the image or the repository. A public preview of the application has a defined port, running time and access control. A dev VM does not replace staging if it differs in topology or integrations.

Complete web application stack The developer works in a private copy of the full stack: frontend, backend, database and supporting services. Migrations, seed, logs, traces and a commit or export complete the work cycle. DEVELOPER DEV VM / PRIVATE COPY FRONTEND BACKEND API DATABASE SERVICES DELIVERY + OPERATIONS MIGRATION + SEED LOGS + TRACES COMMIT / EXPORT

Configuration

We clone the repository using short-lived credentials. The runtime and package manager are pinned, lockfiles are respected, and the stack is started by Compose or the project's bootstrap script. Database migrations and the seed are versioned. Mail, object storage and other dependencies use local services or explicitly specified sandbox endpoints.

Technical scope

An end-to-end change covers the UI, the API contract, a migration and tests. Diagnostics use logs with a correlation ID, healthchecks, and metrics and traces if the application exposes them. A review environment can be shared through expose once the port, running time and access control have been defined.

Outcome and limitations

The developer gets the full stack without installing it on their local computer. Before the VM is deleted, the commit goes to the repository and the required artefacts are exported. A dev VM still differs from staging in topology, secrets management, autoscaling, observability and external integrations.