Skip to content

Sandbox Guide

This guide outlines how to deploy and manage sandboxes for the Whitebox project using Ansible and associated scripts. This includes setup instructions, feature descriptions, and additional information to aid new contributors.

Installation

  1. Install Ansible:

  2. Ensure your SSH keys are added to the server so you can log in without the usual user:pass method.

  3. Clone this repository and navigate to the ansible directory:

    git clone https://gitlab.com/whitebox-aero/whitebox
    cd whitebox/ansible
    
  4. Install Ansible requirements:

    ansible-galaxy collection install -r requirements.yml
    

    Note: This command installs the required Ansible collections locally in your user's home directory (~/.ansible/collections).

  5. Export HCLOUD_TOKEN before deploying or destroying sandboxes. The token must be able to read servers and manage firewalls in the Hetzner Cloud project that hosts the sandbox server.

  6. Configure your hosts.yml file to specify the IP address of the server that Ansible will SSH into. For example:

    sandbox_server:
        hosts:
        sandbox_01:
        ansible_host: <server_ip>
        ansible_user: root
        ansible_port: 22
    

Usage

The sandbox.sh script simplifies deploying and managing sandboxes.

Deploy and destroy reconcile a managed Hetzner Cloud firewall for the configured sandbox host. The firewall allows inbound SSH (22/tcp) and the frontend/backend TCP ports published by currently running sandbox containers. Ports for destroyed sandboxes are removed from the allowlist while ports for remaining sandboxes stay open; outbound firewall rules are not changed.

General Usage

> ./sandbox.sh
Usage: ./sandbox.sh <repo_url|remote_name> <commit_hash|branch_name> <action_type>
Actions: deploy, destroy

Examples:
 ./sandbox.sh https://gitlab.com/whitebox-aero/whitebox 09b1d86e deploy
 ./sandbox.sh origin feature/123 deploy

Note:
1. When given a commit hash while 'destroy', only the sandbox with that commit hash will be destroyed.
2. When given a branch name while 'destroy', all sandboxes with that branch will be destroyed.

Examples

# Deploy using a branch name:
./sandbox.sh origin feature/123 deploy

# Deploy using a commit hash:
./sandbox.sh https://gitlab.com/whitebox-aero/whitebox 09b1d86e deploy

# Destroy sandboxes for a branch:
./sandbox.sh origin feature/123 destroy

# Destroy a specific sandbox by commit hash:
./sandbox.sh https://gitlab.com/whitebox-aero/whitebox 09b1d86e destroy

Media storage and lifetime

Newly templated sandboxes share the Compose-project whitebox-media volume at /opt/whitebox/media: backend is the sole read-write mounter; frontend's nginx mount is read-only. A file seeded into the backend's mount is visible at /media/<relative-path> on the frontend origin. Sharing preserves nginx's existing direct serving instead of relying on Django's debug-only media route. That route currently exists (DEBUG=True); media is unauthenticated through both frontend and backend ports. Use only synthetic/non-sensitive media. The template starts no recording/transfer daemon or RQ worker: sharing files does not produce recordings, and testing seeded files does not validate recording.

  • Volume name is <deploy_tag>_whitebox-media, using the deployment-directory basename as the Compose project identity. deploy_tag is normally the first seven characters of the resolved full commit hash; eight-character input hashes can remain eight. Different project identities isolate storage, not branches or repository paths sharing a tag.
  • Files survive service recreation while the same volume remains. This preserves files, not database state or a complete recording environment. CI replacement does not preserve this volume when branch destroy selects its project.
  • Commit destroy removes the Compose project's volumes only when its backend container is found and has a Compose label; branch destroy discovers running containers only. Neither guarantees orphan cleanup. Orphaned/same-tag volumes may be reused, and image contents at /opt/whitebox/media may be copied into a new volume on its first mount: empty initial storage is not guaranteed. Sandboxes are not archival storage.

Templates come from the invoking checkout. A direct deploy skips templating/start when both services already run; changing the template alone does not repair that running sandbox. CI instead runs branch destroy before deploy, removing selected projects and their volumes before creating the next commit sandbox (see below). Adding a mount does not migrate old backend writable-layer files and may hide them. Recreating the old container removes that writable layer and its files. Preserve needed data separately before any future recreation; this guide supplies no migration procedure. Removing the mounts on rollback does not copy volume data back into container layers, and destroying a sandbox is not a data-preserving upgrade. There is no automatic media preservation across destroy or CI replacement.

Automatic Sandbox Deployment

The shared deploy job in .gitlab/config/shared-ci.yml selects merge_request_event pipelines. Core's run_deploy_sandbox in .gitlab-ci.yml extends it and depends on run_lfs_check. It is not gated by need-product-review, and Draft status does not protect a sandbox from replacement. An MR push can therefore trigger automatic branch destroy then deploy. Branch destroy discovers running containers with the branch label and removes the selected Compose projects and their volumes; this can delete existing sandbox media before the next commit sandbox is created. It does not guarantee orphan cleanup or migrate old media. This is different from direct deploy's both-services-running skip: do not rely on the same volume surviving CI replacement or treat sandboxes as backups.

After deployment, an additional script update_sandbox_info.py updates the MR description with relevant URLs (e.g., frontend/backend access).

Deployment and MR-description updates are automated using GitLab CI/CD and Ansible; this automation does not preserve media from a destroyed sandbox.

Update Sandbox Info Script

The update_sandbox_info.py script updates MR descriptions after sandbox deployment with relevant URLs. This script is triggered automatically after the sandbox is deployed.

For running it over CI, the only requirement is to have GITLAB_TOKEN set in the CI/CD settings.

You can get the token from your GitLab account settings by:

  1. Going to User Settings > Access Tokens.
  2. Creating a new token with api scope.

For running it locally, you would need to set two additional environment variables:

  • CI_PROJECT_ID
  • CI_MERGE_REQUEST_IID

CI_PROJECT_ID can be found by navigating to the project's Settings > General > Project ID. While CI_MERGE_REQUEST_IID can be found in the MR URL. That is the number after the ! in the URL.

You can set these variables in your local shell by first creating a .env file with the following content:

GITLAB_TOKEN=<your_access_token>
CI_PROJECT_ID=<project_id>
CI_MERGE_REQUEST_IID=<merge_request_id>

Then, you can set the environment variables by running:

source .env

Install the required packages by running:

poetry install

Finally, you would write logs of ansible playbook to a file by appending 2>&1 | tee deploy.log in the end of the deploy command. Example:

./sandbox.sh origin 09b1d86e deploy 2>&1 | tee deploy.log

And then run the script:

poetry run python update_sandbox_info.py

During development, you can run tests by running:

make test