Skip to main content
Zyra Zyra
Features Pricing Security FAQ Documentation
Sign In Sign up for free

Documentation › User Guides › Organizations › Stage 2 › Deploy your first Virtual Server

Deploy your first Virtual Server

Chapter 5 · about 8 minutes to read

This is chapter 5 of Stage 2 — the "aha moment" chapter. You're going to ask Zyra to start a real container on the Compute Node you just enrolled and watch it transition from creating to running with a working access URL, all in about a minute.

Time: about 10 minutes including reading. Prerequisites: at least one Compute Node in Ready state, Docker available on that node (it almost always is by now — Docker Desktop on Windows/macOS, Docker Engine on Linux).

What a Virtual Server actually is

A Virtual Server (VS) is a persistent Docker container managed by the Zyra orchestrator. It has a lifecycle (creating → pulling_image → starting → running), a target Compute Node, declared resources, and zero or more access endpoints (web terminal, SSH, RDP). Schema-wise it's the virtual_servers table in backend/app/models/virtual_server.py: name, docker_image, vcpus, memory_mb, disk_gb, exposed_ports, volumes, target_device_id, status.

Unlike a job (chapter 6), a Virtual Server is designed to keep running indefinitely. You stop it when you're done.

Step 1 — Open the create form

In the dashboard sidebar, click Virtual Servers → Create Virtual Server. The button is also one of the welcome cards on the empty-state dashboard you saw in Stage 1, and it unlocks the moment your first Compute Node is in Ready state.

[SCREENSHOT: Virtual Servers list with "Create Virtual Server" button highlighted]

Step 2 — Fill in the form

The form maps directly to the VirtualServerCreate schema. Required fields:

  • Name — anything 1-255 characters. Try first-test for this walkthrough.
  • Docker image — the container image to run. For your first deploy, use one of these known-working images:
    • nginx:latest — smallest, fastest, lowest-risk first deploy. Comes up on port 80.
    • scottyhardy/docker-remote-desktop:latest — a known-working RDP image used in Zyra's own production validation. Larger pull (~2 GB) but gives you a full desktop session.
    • ubuntu:24.04 with no command — sits idle, useful for testing the web terminal.
  • Target Compute Node — pick the device from chapter 4. You can also leave it as "Any available" and the scheduler will pick the highest-capability Ready device.
  • vCPUs — 1-128, default 2. Two is fine for the first deploy.
  • Memory (MB) — 512-131072 (128 GB), default 2048.
  • Disk (GB) — 10-4096, default 100.

Optional fields:

  • Exposed ports — list of {container_port, host_port, protocol} tuples. For nginx:latest, add one: container 80, host 8080, TCP.
  • Volumes — named persistent volumes mounted into the container. Skip for the first deploy.
  • Enable web terminal — on by default. Lets you docker exec into the container from your browser.
  • Enable SSH / Enable RDP — opt-in toggles. Skip unless you picked the docker-remote-desktop image, in which case turn RDP on.
[SCREENSHOT: Create Virtual Server form with the fields above filled in]

Step 3 — Click Deploy

The dashboard calls POST /api/v1/virtual-servers/ with your form values. The backend (backend/app/routers/virtual_servers/create.py) validates the payload, creates the virtual_servers row in creating status, and dispatches deploy_to_device(vs, db) which messages the agent on the target Compute Node.

You're redirected to the VS detail page. The status pill cycles through states you can watch in real time:

  1. creating — row inserted, dispatch sent.
  2. pulling_image — agent is docker pull-ing the image. This is the slow step for big images (the RDP image is ~2 GB).
  3. creating_container — image pulled, agent calls docker create with the resource limits.
  4. starting — docker start issued.
  5. running — container is up. The detail page now shows the connect URL.

For nginx:latest the whole pipeline typically completes in 20-30 seconds. For the RDP image, 60-90 seconds on a typical home upload pipe.

[SCREENSHOT: VS detail page mid-deploy, status badge showing "pulling_image" with progress text]

Step 4 — Connect

Once the status is running, the Connect panel shows up:

  • Web terminal — opens a browser-based shell inside the container (via the ContainerAccessHandler on the agent side).
  • HTTP — if you exposed port 8080, you'll see a link like https://app.getzyra.io/vs/{vs_id}/proxy/8080/. For nginx, opening it shows the default "Welcome to nginx!" page running on hardware you own.
  • RDP — if enabled, a one-click launcher that downloads an .rdp file with the credentials pre-filled.

This is the moment. You asked for compute, Zyra found a device, pulled an image, started a container, and routed a connect URL back to you — and the device is sitting under your desk or in your office rack, not in a hyperscaler datacenter.

[SCREENSHOT: VS detail page in "running" state with web-terminal connect button]

Step 5 — Check the meter

Scroll to the Cost panel. The default rate is $0.10/hour (stored as cost_per_hour Numeric(10,6) on the virtual_servers row). The meter increments while the VS is running and stops when you stop or terminate it. Stage 3 chapter 4 covers invoices in detail; for now, just notice that the meter started the moment you clicked Deploy.

What just happened

You ran a real, network-reachable Docker container on a device your organization controls. The agent honoured the CPU/RAM/disk caps you set in chapter 4. The orchestrator routed your connect URL through the Zyra control plane so you don't need to expose ports on the Compute Node itself. Billing is per-second, prorated to the configured hourly rate.

Troubleshooting

  • Stuck on pulling_image for minutes. Big images on slow uplinks, or a Docker Hub rate limit on the Compute Node. Watch the agent log for pulling image … and pull rate limit exceeded lines.
  • creating_container → error_message: Cannot connect to the Docker daemon. Docker isn't running on the Compute Node, or the agent user isn't in the docker group. Fix and re-deploy; the existing row will retry.
  • Web terminal won't connect. The ContainerAccessHandler opens a WebSocket back to the backend; corporate proxies that strip Upgrade headers will block it. Try from an unfiltered network.
  • "No Ready devices match constraints". The scheduler couldn't find a Compute Node with enough free vCPU/RAM/disk. Check the capability caps you set in chapter 4 — they may be tighter than this VS needs.

← Previous: Enroll your first Compute Node

Next: Run your first job →

Last reviewed: 2026-05-21

© 2026 Zyra. All rights reserved. | Privacy Policy | Terms of Service | Careers