- Understanding your role and responsibilities in the support and maintenance of your organization’s MacStadium VDI architecture
- Navigating core management interfaces (the management UI and Ansible, Citrix Cloud, Orka Engine)
- Executing common operational tasks (provisioning desktops, managing images, troubleshooting)
- Establishing operational best practices for your MacStadium VDI environment
Your role in managing MacStadium VDI
As an administrator, you will be responsible for managing the three interconnected systems that make up MacStadium VDI. These are: Orka Engine (virtualization layer):- Hosts macOS VMs on Apple silicon hardware
- Manages VM lifecycle (create, start, stop, delete)
- Handles image storage and distribution
- Brokers user connections to desktops
- Manages Machine Catalogs and Delivery Groups
- Controls access policies and user assignments
- Automates VM deployment and lifecycle operations
- Maintains configuration consistency
- Coordinates image management across hosts
- Runs from the command line on your Ansible controller, or from the browser-based management UI, which runs the same playbooks as task templates
Key concepts and terminology
Orka-specific terms:- Orka host: Physical Mac running Orka Engine (for example, Mac mini M4, Mac Studio)
-
VM name prefix: Orka Engine has no group object. Instead, give related VMs a shared name prefix (for example,
citrix-vda-finance-01andcitrix-vda-finance-02).list.yml,vm.yml, anddelete.ymlmatch VM names by prefix, so you can list, start, stop, or delete those VMs together. - Golden image: Template VM with macOS, Citrix VDA, and applications pre-installed
- Max VMs per host: 2. Apple’s macOS EULA allows no more than two macOS VMs per host, and the deployment playbook enforces that cap.
-
Bridged networking: VMs receive IPs directly from your network. Set
network_interface(for example,en0) when you deploy and every time you start a VM. VMs deployed or started without it use NAT, which Citrix Cloud and end users can’t reach.
- Machine catalog: Collection of VMs with similar configuration. Naming the VMs in a catalog with a shared prefix keeps them easy to manage together in Orka.
- Delivery group: Links machine catalog to users and defines access policies
- VDA (Virtual delivery agent): Software on macOS VM that registers with Citrix Cloud
- Enrollment token: Time-limited credential for VDA registration
- Rendezvous protocol: Enables HDX connections to non-domain-joined VMs
-
Inventory: List of Orka hosts (the
dev/inventoryfile in yourorka-engine-orchestrationclone) -
Playbook: YAML file that performs one operation (for example,
deploy.yml,vm.yml,install_citrix_vda.yml) -
Group vars: Configuration variables shared across hosts (
dev/group_vars/all/main.yml, next todev/inventory) -
Tags: Selective playbook execution (for example,
--tags planfor dry runs) - Management UI: Browser-based interface on your Ansible controller that runs the same playbooks as task templates, such as Virtual Machines | Deploy VM. Each template prompts for its inputs when you click Run.
Architecture overview:

Getting Started
Ansible access and credentials setup
You will want to verify that you have access to all three MacStadium VDI management layers.Ansible control node:
The Ansible control node (also known as an “Ansible controller” or “Ansible runner”) is where you run playbooks and the management UI to manage Orka hosts. SSH to the Ansible control node and change to the directory where you cloned the orchestration repository during setup:ansible-core 2.18.4 with pipx.
The management UI runs on the controller on port 3000. From the controller itself, open http://localhost:3000 and log in with the admin credentials from semaphore/.env.
Credentials used by the orchestration:
Ansible connects to the Mac hosts over SSH with the key you copied to each host during setup (~/.ssh/id_ed25519 if you accepted the default path). There’s no separate Orka Engine API or API token: each playbook runs orka-engine commands on the hosts over SSH.
The playbooks take the following secrets as variables.
Store these as secrets in the management UI or pass them at run time with
-e. Don’t commit them to Git. In the management UI, the Key Store holds the Mac hosts SSH key, the Base VM Credentials variable group holds vm_username, vm_password, citrix_installer_url, and hostname_suffix, and the OCI Credentials variable group holds oci_username and oci_password. configure_semaphore.py sets these values during setup. The VDI | Register Citrix VDA template prompts for enrollment_token each time you run it.
Common Ansible tasks:
-
Deploying a VM:
ansible-playbook deploy.yml -i dev/inventory -e "vm_name=<VM_NAME>" -e "vm_image=<IMAGE>" -e "network_interface=en0" -
Managing VM state:
ansible-playbook vm.yml -i dev/inventory -e "vm_name=<VM_NAME>" -e "desired_state=<STATE>". When<STATE>isrunning, also pass-e "network_interface=en0". -
Installing and registering the Citrix VDA:
install_citrix_vda.yml, thenregister_citrix_vda.yml -
Creating images:
ansible-playbook create_image.yml -i dev/inventory -e "vm_image=<BASE_IMAGE>" -e "remote_image_name=<IMAGE_DESTINATION>" -
Checking hosts:
ansible-playbook validate.yml -i dev/inventory --tags images -e "validate_image_name=<IMAGE>"confirms an image is cached on every host
validate.yml is also a task template in the management UI. For every playbook and its variables, see the Ansible quick reference.
Citrix Cloud Console access and credentials setup
Citrix Cloud Console is the web-based management control plane for Citrix DaaS. Access URL: https://citrix.cloud.comLogin: Your Citrix Cloud administrator credentials
Key sections:
- Web studio: Machine catalogs, delivery groups, enrollment tokens
- Monitor: Session metrics, connection quality, active users
- Policies: HDX settings, access control, feature restrictions
Credentials needed:
- Citrix Cloud administrator account
- Customer ID (visible in Cloud Console, used in VDA configuration)
Common tasks performed in Citrix Cloud Console:
- Generating enrollment tokens
- Creating or modifying Delivery Groups
- Assigning users to desktops
- Monitoring session health
Orka Hosts
As an IT administrator, you will need direct SSH access to physical Macs running Orka Engine. This access is used for troubleshooting, not routine operations. You can access your Orka Engine hosts by SSHing directly to the host IP:- Debugging Orka Engine issues
-
Checking disk space:
df -h /opt/orka -
Reviewing Orka Engine logs:
tail -100 /opt/orka/logs/com.macstadium.orka-engine.server.managed.log -
Manually inspecting VMs:
orka-engine vm list
-
SSH key: The same key Ansible uses (
~/.ssh/id_ed25519if you accepted the default path during setup) -
User:
administrator, or whichever user you set asansible_userduring deployment
OCI registry
An OCI registry is a container registry for storing your organization’s golden images. This can be public (GHCR) or private (Harbor, Artifactory, etc.)MacStadium’s public GHCR registry:
-
URL:
ghcr.io/macstadium/orka-images - No authentication required
-
Base images:
sonoma:latest,sequoia:latest,tahoe:latest
Private OCI registry:
-
URL: The full image reference you pass as
vm_imageorremote_image_name(for example,registry.example.com/citrix-vda/sonoma-finance:v2.0) -
Credentials:
registry_usernameandregistry_passwordforpull_image.ymlandcreate_image.yml;oci_usernameandoci_passwordfordeploy.ymlandpush_image.yml - Contains: Your golden images with Citrix VDA pre-installed
Common tasks performed when managing OCI images:
-
Pulling images to every host:
pull_image.yml -
Building and pushing a golden image:
create_image.yml -
Pushing an image from a running VM:
push_image.yml -
Listing images cached on the hosts:
list_images.yml -
Removing a cached image from the hosts:
delete_image.yml
Citrix Cloud Console Navigation
After logging in to https://citrix.cloud.com:-
Hamburger menu: Navigate between services
- Select “DaaS” or “Virtual Apps and Desktops” to access Web Studio
-
Web Studio:
- Machine Catalogs: View registered VMs, enrollment tokens
- Delivery Groups: User assignments, published desktops
- Policies: HDX settings, security policies
-
Monitor tab:
- Real-time session data
- Connection quality metrics
- Historical trends
-
Support menu:
- Documentation links
- Support case management
- Machine Catalogs → Select your catalog → Verify VMs show “Registered”
- Monitor → Check session launch success rate >95%
- Monitor → Verify there have been no “Unregistered” machines in past 24 hours
Connecting to the Ansible Management Node
Initial connection:Testing Ansible connectivity
If Ansible connectivity fails:
-
Verify the SSH key exists:
ls -la ~/.ssh/id_ed25519 -
Test direct SSH connectivity:
ssh administrator@10.0.100.10 - Check that your Ansible inventory file has the correct IPs listed
Key Ansible variables:
-
ansible_user: SSH user on the Mac hosts (administratorin the getting started guides) -
vm_image: Default golden image to deploy from -
max_vms_per_host: VM capacity limit. This is2, the maximum that Apple’s macOS EULA allows. -
engine_binary: Path to the Orka Engine binary (/usr/local/bin/orka-engine) -
network_interface: (Optional) Host network interface for bridged networking, for exampleen0
Reviewing Mac Host inventory
-
List all Orka hosts:
ansible hosts -i dev/inventory --list-hosts -
Check that Orka Engine is running on all hosts:
ansible hosts -i dev/inventory -m shell -a "/usr/local/bin/orka-engine info". It exits with an error if the Engine service isn’t reachable, and prints the Engine version and data directory. - List every VM and the host it runs on:
list.yml prints one line per VM in the following format:
-e "vm_name=<PREFIX>", it lists only the VMs whose names start with that prefix, and each line starts with the prefix you passed. Count the VMs per host to see where you have free slots: each host can run two.
- Check how much capacity is free without deploying anything:
- Check available disk space:
ansible hosts -i dev/inventory -m shell -a "df -h /opt/orka"
- <70% used: Healthy
- 70-85% used: Plan for cleanup or expansion
-
85% used: Action required (delete old images, expand storage)
- Host IP
- Hardware model (Mac mini M4, Mac Studio M2 Ultra, etc.)
- Current VM count
- Available capacity
- Network interface (10Gb vs 1Gb)
- Notes (for example, “Reserved for engineering team”)
Core workflows
Provisioning a new desktop
- The user account exists in your identity provider (Azure AD, Okta, etc.)
- The user is assigned to the appropriate Delivery Group in Citrix Cloud
- There is available capacity on your Orka hosts (at least one host running fewer than two VMs)
- The appropriate golden image is cached on the hosts. This is optional, but deployments start faster when the image doesn’t need to be downloaded.
- You have a Citrix enrollment token for the Machine Catalog. See Citrix DaaS configuration.
1
Select the golden image
Choose the image based on the user’s department, role, and access needs. To list the images cached on each host, run Each image prints as (This can take 5-15 minutes depending on the image size and your network speed)
list_images.yml (management UI: Images | List Images):<IMAGE>:<TAG> | Size: <SIZE> (used: <USED>) | Host: <HOST>.If the golden image isn’t cached, pull it to every host with pull_image.yml (management UI: Images | Pull Image). Add -e "registry_username=<REGISTRY_USERNAME>" -e "registry_password=<REGISTRY_PASSWORD>" if the registry requires authentication. The management UI template doesn’t prompt for these, so use the CLI for a registry that requires them.2
Deploy the VM
deploy.yml deploys one VM, named with the vm_name you pass. If a VM with that name already exists, it doesn’t deploy a new one. Choose a name that starts with the prefix you use for this group of desktops, such as citrix-vda-finance-11.Always run --tags plan first:Set
network_interface to the host’s network interface, for example en0, for every VDI VM (bridged networking). The examples on this page use en0. Without it, the VM uses NAT, and Citrix Cloud and end users can’t reach it. Orka Engine doesn’t save the network interface with the VM, so pass it again every time you start the VM.-e "public_image=false" -e "oci_username=<OCI_USERNAME>" -e "oci_password=<OCI_PASSWORD>" so the playbook pulls it with credentials.The playbook selects a host with a free slot, starts the VM, waits for it to get an IP address, and prints the VM’s name, IP, state, CPU, and memory. It doesn’t install or register the Citrix VDA: the next two steps do that.To check on the VM from another terminal, run:3
Install the Citrix VDA
Run this step even if your golden image already has the Citrix VDA installed: it’s the only step that sets the VM’s hostname to The playbook installs the .NET runtime prerequisites, sets the VM hostname, installs the VDA, and reboots the VM. Wait for the reboot to finish before you register the VDA. For details on each variable, see Citrix DaaS configuration.
vm_name, and VMs deployed from the same image otherwise all keep the image’s hostname.Run install_citrix_vda.yml (management UI: VDI | Install Citrix VDA):4
Register the VDA with Citrix Cloud
Run
register_citrix_vda.yml with your enrollment token (management UI: VDI | Register Citrix VDA):install_citrix_vda.yml and register_citrix_vda.yml act only on the VM whose name exactly matches vm_name.5
Verify VDA registration in Citrix
- Navigate to Citrix Cloud Console → Web Studio → Machine Catalogs
- Select your Machine Catalog (for example, “Orka-macOS-Sonoma-Finance”)
- Refresh the machine list
- New VMs should appear with:
- Registration state: ‘Registered’
- Power state: ‘On’
- Fault state: ‘None’
/Library/Application Support/Citrix/VDA/Logs/ on the VM. To troubleshoot VDA registration failures, see the Troubleshooting quick reference.6
Assign to user/delivery group
If you’re using a static/dedicated assignment:The desktop should appear in Citrix Workspace immediately after being assigned to a Delivery Group, and should launch within 15-30 seconds. When the provisioning workflow has completed, the user will now have access to their macOS virtual desktop.
- In Citrix Cloud Console → Web Studio → Delivery Groups
- Select Delivery Group (for example, “Finance-macOS-VDI”)
- Click “Edit Delivery Group” → “Users” tab
- Add user: Search for user account, select, assign to specific VM
- Having the user launch the Citrix Workspace application
- User logs in
- Desktop should appear: “Mac OS Finance Desktop” (or configured display name)
- User clicks desktop → session launches successfully
provision_user.yml (management UI: Virtual Machines | Provision User to VM):Managing images
Golden images are the foundation for rapid desktop provisioning. The image lifecycle is typically:- Create: Build a golden image with macOS + VDA + applications
- Test: Deploy a test VM and validate its functionality
- Publish: Push your image to an OCI registry with an applicable version tag
- Cache: Pull an image to all Orka hosts
- Deploy: Use an image for new VM provisioning
- Update: Apply patches and create a new image version
- Retire: Remove old image versions after migration to a new version is complete
-
Configuration scripts are prepared in the
scripts/directory at the root of yourorka-engine-orchestrationclone - A base macOS image is available
-
You have push credentials for your registry (
registry_usernameandregistry_password)
registry_username or registry_password, so run create_image.yml from the CLI when your registry requires them.
The create_image.yml playbook automates the following:
-
Deploy a temporary VM named
image-hostfrom the base image you pass asvm_image -
Install macOS updates, if you pass
upgrade_os=true - Run each configuration script on the VM
-
Save the VM as an image and push it to the registry destination you pass as
remote_image_name, including its tag - Delete the temporary VM
scripts/ directory, so create it first:
*.sh file directly inside scripts/ (it doesn’t look in subdirectories). It doesn’t sort the scripts, so don’t rely on the order they run in: if one step depends on another, put both in the same script. An example script structure would be:
install_vscode.sh), which copies an app bundle into /Applications without sudo:
-
Scripts run on the VM as the VM’s
adminuser, withoutsudo. Build scripts can’t run root commands:adminin the MacStadium base images doesn’t have passwordless sudo, and the scripts run without a terminal or password, so asudocommand, such assudo installerfor a.pkg, fails the image build. -
The playbook connects to the VM as
adminwith the passwordadmin. If your base image uses different credentials, pass them with-e "configure_vm_hosts_vm_user=<VM_USER>" -e "configure_vm_hosts_vm_password=<VM_PASSWORD>". - Test scripts individually before image creation
-
Include error handling (
set -e, exit codes) - Clean up temporary files
create_image playbook:
-
The playbook deploys a temporary VM named
image-hoston a host with a free slot - It waits for the VM to get an IP address, then connects to it over SSH through the host
-
If you passed
upgrade_os=true, it installs macOS updates -
It runs each script in
scripts/ -
It saves the VM as an image and pushes it to
registry.example.com/citrix-vda/sonoma-finance:v2.0 -
It deletes the temporary VM with
delete.yml
install_citrix_vda.yml, which also sets the hostname, and register it with Citrix using register_citrix_vda.yml, as described in Provisioning a new desktop. You will want to verify that the VM has deployed successfully, the VDA registers with Citrix Cloud, applications launch correctly, there are no errors in the VDA logs, and test users can launch sessions and access applications. If issues are found, you can fix any script errors in the scripts/ directory, delete the failed image from your container registry, and re-run create_image.yml with the corrected scripts.
Once the image has been validated, deploy production VMs from the tested version tag by passing it as vm_image, for example registry.example.com/citrix-vda/sonoma-finance:v2.0. Don’t re-run create_image.yml to apply a :latest tag: each run rebuilds the image from the base image and your scripts, so the result isn’t the build you tested.
You can also set the version as the default vm_image in your group variables file.
To capture a VM that you configured by hand as an image, use push_image.yml (management UI: Images | Push Image) instead. vm_name must exactly match the VM’s name:
Updating Existing Images
Patch an existing golden image:-
Build a new image version with
create_image.yml, passing your current production image (or a fresh base image) asvm_imageand a new version tag inremote_image_name - Test the image thoroughly
-
Update the
vm_imagedefault to the new version - Recreate VMs from the new image
vm_image only affects VMs you deploy afterward. Existing VMs keep running the image they were deployed from, whether they’re pooled or dedicated, until you delete and redeploy them.
For non-persistent (pooled) desktops:
-
Update the
vm_imagedefault image -
Delete and redeploy the pooled VMs, during off-hours if possible, then run
install_citrix_vda.ymlandregister_citrix_vda.ymlon each new VM
- Schedule a maintenance window
- Delete old VMs
- Deploy a VM from the newly created image
-
Run
install_citrix_vda.yml, which also sets the hostname, then register the new VM with Citrix - Restore user data from backups (if applicable)
Caching Images Across Hosts
Why cache:- Faster VM deployment (no download wait)
- Reduces network bandwidth during deployments
- Ensures consistency across hosts
Cache images on all hosts:
Verify cached image:
validate.yml with the images tag. The check matches the image name and tag separately, so confirm with list_images.yml if you have several images with the same tag:
Cache multiple images:
Runpull_image.yml once per image:
Remove old image versions to free space:
Check disk usage, then delete the old image withdelete_image.yml (management UI: Images | Delete Image). Use the image name and tag exactly as list_images.yml shows them. If you leave off the tag, the playbook deletes the latest tag. Add -e "node_name=<HOST_IP>" to delete the image from one host only.
Version control and rollback
Version numbering scheme:
Use semantic versioning:MAJOR.MINOR.PATCH
-
v1.0.0: Initial release -
v1.1.0: Added new application -
v1.1.1: Security patch -
v2.0.0: Major update (macOS version upgrade)
vm_image in your group variables file (for example, to roll back to v1.2.1):
--tags plan run shows which VMs on which hosts will be deleted, without deleting anything.
deploy.yml deploys one VM per run, so redeploy each VM that list.yml showed, by name. For example, if the group was citrix-vda-finance-01 through -10:
deploy subcommand in Bulk VM lifecycle management. It names each VM with your prefix and a random suffix, so the new VMs don’t keep their old names. After the VMs are deployed, run install_citrix_vda.yml on each VM, which also sets its hostname, and register each VM with register_citrix_vda.yml.
You will want to communicate this change to users: “We’ve rolled back to the previous desktop image version due to [the previously identified issue]. Your desktop has been recreated from the previous image.” Rollback time is estimated to take between 30-60 minutes for 20-30 VMs.
User assignment and policies
This involves managing which users can access specific desktops, and what they can do within active sessions.Working with Citrix Delivery Groups
Delivery Groups link Machine Catalogs (VMs) to your users. To view existing Delivery Groups: Navigate to Citrix Cloud Console → Web Studio → Delivery Groups. You should then see a list of configured Delivery Groups. For example:Finance-macOS-VDI: 50 VMs, pooled, 150 users
Create a new Delivery Group
In a scenario where a team needs new macOS desktops within your MacStadium VDI instance, deploy the VMs with a shared name prefix, such ascitrix-vda-marketing-, then install and register the Citrix VDA on each one. deploy.yml deploys one VM per run, as shown in Provisioning a new desktop. For 15 VMs, use the deploy and install-citrix subcommands in Bulk VM lifecycle management, then register each VM with register_citrix_vda.yml.
After the VMs show as Registered in their Machine Catalog, proceed through the following steps:
- Navigate to Citrix Cloud Console → Delivery Groups → Create New Delivery Group
- Select machines by choosing the Machine Catalog containing the new VMs created in the previous step(s)
- Add users by clicking “Add”, searching for the specified team, and selecting the correct AD/Azure AD group. Alternatively, you can add users individually.
- To configure desktops, give them a display name (for example, “macOS Marketing Desktop”) and add a description for the VM.
- Choose Published Desktop as the delivery type
Modify existing Delivery Groups
Add a user to an existing group:- Delivery Groups → Select group → Edit
- Users tab → Add → Search for the selected user → Assign
- Delivery Groups → Select group → Edit
- Users tab → Select user → Remove
Assignment types:
Static (dedicated):
- Each user is assigned permanently to a specific VM
- The VM retains the user’s settings, files (if they are stored locally)
- Best used for: Developers, power users
Pooled (random):
- User gets the next available VM
- The VM isn’t reset between sessions. Changes a user makes stay on the VM until you delete and redeploy it.
- Best used for: Individual task workers and call centers
Change assignment type:
Typically, you cannot change an existing Delivery Group’s assignment type. Instead, you can do the following:- Create a new Delivery Group with the desired assignment type
- Migrate your users to the new group
- Delete the old Delivery Group
Access Control and Entitlements
These settings control which users can access desktops and from where.User entitlements:
In Citrix,entitlements are what grant users permission to access specific resources.
Grant access:
- Assign the user to a Delivery Group (see Provisioning a new desktop, earlier on this page)
- The user sees the assigned desktop in Citrix Workspace immediately
Revoke access:
- Remove the user from a Delivery Group
- The desktop disappears from the user’s Workspace within 5 minutes
Conditional access:
Require specific conditions for desktop access:- Citrix Cloud Console → Web Studio → Policies
- Create a new policy, or edit an existing policy
-
Configure conditions:
- Device type: macOS, iOS, web only
- Network location: Internal network only, or allow external network
- Device compliance: Require managed device (if using MDM) Example policy: “The finance team can only access desktops from inside the corporate network or via a VPN”
Multi-factor authentication (MFA):
This is enforced at the identity provider level (Azure AD, Okta), not in Citrix directly. Configure MFA from within your IdP:- Require MFA for Citrix Workspace access
- Users will be prompted for a second factor during login
Session Policies and Preferences
These control user session behavior.Session timeout:
- Navigate to Citrix Cloud Console → Web Studio → Policies
- Edit the policy for the necessary Delivery Group
-
Configure session settings:
- Session idle timeout: Disconnect after X hours idle (default: 24 hours)
- Disconnected session timeout: Log off user(s) after X hours disconnected (default: never) Recommended settings:
- Pooled desktops: 8 hours idle, 30 minutes disconnected (aggressive cleanup)
- Dedicated desktops: 24 hours idle, never disconnect (preserve work)
Reconnection:
This allows users to reconnect to disconnected sessions:- Policy → Session → Connection → Enable “Session reconnection”
- When users close Workspace without logging out, their session is preserved
- When users reopen their Workspace, they can reconnect to same session
Feature policies (HDX settings):
These control what users can do within sessions. Common policies:Clipboard:
- Bidirectional: Copy/paste both ways (client ↔ VM)
- Client to VM only: Users can paste into the VM, but not copy out
- Disabled: No clipboard access
- Policies → HDX Settings → Clipboard redirection
- Select: Allowed / Prohibited / Client to VM only
File transfer:
- Allow: Users can drag files between client and VM
- Prohibit: No file transfers are allowed
- Policies → HDX Settings → Client drive redirection
- Select: Allowed / Prohibited
USB redirection:
- Allow specific devices: Keyboards, mice, smart cards
- Prohibit all: Block USB entirely
- Policies → HDX Settings → USB device redirection
- Select device classes allowed
Printing:
- Client printer mapping: The user’s local printers appear in the VM
- Disable: No printing
- Policies → Printing → Client printer redirection
- Select: Allowed / Prohibited
Example policy set (Finance team - secure):
- Clipboard: Client to VM only (prevent data exfiltration)
- File transfer: Prohibited
- USB: Prohibited
- Printing: Allowed
- Session timeout: 8 hours idle
Example policy set (Engineering team - open):
- Clipboard: Bidirectional
- File transfer: Allowed
- USB: Allowed (specific devices)
- Printing: Allowed
- Session timeout: 24 hours idle
Apply policies to a specific Delivery Group:
- Create a policy with the desired settings configured
- In Citrix Cloud Console, navigate to Policies → Filter → Set filter to Delivery Group name
- The chosen policy applies to all users in that group
Lifecycle operations
These consist of day-to-day VM management operations. You will likely need to Start, Stop, and Delete VMs on a regular basis as needed to troubleshoot VM issues, apply configuration changes requiring a restart, and to help conserve resources during off-peak hours. Usevm.yml (management UI: Virtual Machines | Manage VM) for all three. vm.yml acts on every VM whose name starts with the vm_name you pass, so vm_name=citrix-vda-finance- starts or stops the whole group. To act on one host only, add --limit <HOST_IP>.
Start a VM:
network_interface every time you start a VM, including when vm.yml starts a whole prefix group. Orka Engine doesn’t save the network interface with the VM, so a VM started without it comes up on NAT, where Citrix Cloud and end users can’t reach it. The Virtual Machines | Manage VM template has no network_interface field, so set network_interface: en0 in dev/group_vars/all/main.yml, which the management UI loads too.
Stop a VM:
Delete a VM:
There’s no exact-match delete. For example,vm_name=citrix-vda-finance-1 also matches citrix-vda-finance-10 through citrix-vda-finance-19. Run list.yml with the same vm_name first, and only delete when the list shows just the VMs you intend to remove. Running VMs are stopped before they’re deleted.
delete and deploy subcommands in Bulk VM lifecycle management.
To recreate an estimated 10 VMs, this process will take 30-60 minutes to run. User impact will vary depending on if VMs are pooled or dedicated. If a VM is pooled, user impact should be minimal if recreation is done during off-hours. If a VM is assigned to a dedicated user, that user will lose their local data unless it is backed up upon VM recreation. It is considered a best practice to schedule VM reconfiguration during specific maintenance windows, and to notify users in advance.
When considering the VM deprovisioning and cleanup process, you will want to remove desktop access when a user leaves your organization, or a VM is no longer needed.
To remove users from a Citrix Delivery Group:
- Navigate to Citrix Cloud Console → Delivery Groups → Select group
- Edit → Users tab → Remove user
ssh -J administrator@<HOST_IP> <VM_USERNAME>@<VM_IP>), copy the user’s home directory, and delete the VM after the backup has been confirmed.
To delete an entire set of VMs that share a prefix, preview the deletion plan, then run delete.yml (management UI: Virtual Machines | Delete VM):
delete subcommand in Bulk VM lifecycle management.
VM cleanup task checklist:
- Remove User from the Citrix Delivery Group
- Delete the VM from Orka
- Archive User data (if necessary)
- Update capacity tracking spreadsheet/software
- Confirm that the VM has been removed from the Machine Catalog in Citrix
Monitoring and Troubleshooting
Key metrics to watch:
Check daily:
-
VM registration status. Check this by navigating to Citrix Cloud Console → Monitor → Machine Catalogs. You will want to look for:
- Unregistered Machines: Should be 0
- Power state “Off”: Investigate why VMs are powered off
- Faults: Any VM faults which require attention
-
Session launch success rate. Check this by navigating to Citrix Cloud Console → Monitor → User Activity.
- Target: >95% success rate.
- If User Activity is <95%: Review failed connection attempts, identify any patterns to help determine the cause (specific users, times issues occur, VMs impacted)
-
Host capacity. This can be checked by running
ansible-playbook list.yml -i dev/inventoryand verifying:-
Host capacity: Count the running VMs per host in the
Hostcolumn. A host running two VMs is at capacity. -
Disk usage: Run
ansible hosts -i dev/inventory -m shell -a "df -h /opt/orka"and confirm usage is below 85%. - If usage is above 85%, plan for disk expansion or cleanup.
-
Host capacity: Count the running VMs per host in the
Check weekly:
-
Performance trends. You can review these by navigating to Citrix Cloud Console → Monitor → Trends. Review the following:
- Average session launch time (target: <30 seconds)
- Session quality (should be “Good” or “Excellent”)
- Peak concurrent users (plan capacity for 20% growth)
-
Host health can be verified by confirming available disk space using:
ansible hosts -i dev/inventory -m shell -a "df -h /opt/orka"- Check for any errors in the Orka Engine logs:
ansible hosts -i dev/inventory -m shell -a "grep -i error /opt/orka/logs/com.macstadium.orka-engine.server.managed.log | tail -20"
- Check for any errors in the Orka Engine logs:
-
Image currency
- Are VMs running the latest golden image version?
- Are there any security patches pending?
- If yes, schedule any updates that are needed and communicate any downtime to Users.
Check monthly:
-
Capacity planning
- Confirm your current VM count vs. your total VM capacity
- Calculate anticipated user growth trends
- Use these calculations to help determine your projected need for additional hosts
-
User feedback
- Are there any session quality complaints? If so, what are they, and are there any trends to surface? Track these in an internal spreadsheet or other tool for additional sentiment analysis.
- Application performance issues
- Feature requests or service improvements
-
Security review
- Verify whether enrollment tokens have been rotated in the last 90 days
- Confirm SSH keys are up-to-date
- Review audit logs
-
Alert thresholds
- Set up relevant monitoring alerts via email/Slack/Teams/etc.
-
Confirm which alerts require immediate response (Critical)
- VDA registration drops below 80% of expected targets
- All VMs in a Delivery Group are unavailable
- Host disk space >90%
-
Confirm which alerts require a response within 4 hours:
-
10% of sessions launched fail over 15 minutes
- Host CPU sustained usage >80% for 30 minutes
- Available disk space >75%
-
- If session launch time exceeds baseline by 50%, or a new “Unregistered” VM appears, review this information daily.
Common issues and resolutions
VM deployed, but is not registered in Citrix:
Symptoms:-
The VM appears in the
list.ymloutput - The Citrix Machine Catalog shows no new VM
- Desktop is not available in Workspace
-
Confirm the VDA was installed and registered.
deploy.ymlonly deploys the VM: runinstall_citrix_vda.yml(even if the golden image includes the VDA, since it sets the hostname), and then runregister_citrix_vda.ymlfor every new VM. See Provisioning a new desktop. -
Check the VDA service is running. Get the VM’s IP address and host from
list.yml, connect to the VM through its host, and list the Citrix services:
-
Check enrollment token:
- Citrix Cloud → Machine Catalogs → Enrollment Token
- Verify that the token is not expired
-
If the token is expired: Generate a new enrollment token in Web Studio and re-run
register_citrix_vda.ymlwith the newenrollment_token. You don’t need to redeploy the VM.
-
Check the Citrix VDA logs in
/Library/Application Support/Citrix/VDA/Logs/on the VM. Look for any registration errors, and follow up on any that appear. - Test network connectivity from the VM, in the same SSH session:
*.xendesktop.net and outbound TCP/UDP 443 to *.*.nssvc.net.
If this problem persists, review the Troubleshooting quick reference.
Issue: Session launch timeout
Symptoms:- User clicks on the desktop in their Citrix Workspace
- A connection progress bar appears
- The session eventually times out with a “Cannot connect” error
-
Verify the VM is running:
ansible-playbook list.yml -i dev/inventory -e "vm_name=<VM_NAME>" - Confirm the Citrix Machine Catalog shows that the VM is both “Registered” and “Available”
-
Check Rendezvous connectivity. The VM needs outbound access to
*.*.nssvc.neton TCP/UDP 443, and no inbound ports. Confirm that your firewall allows that traffic and that no HTTP proxy is blocking WebSocket connections. See Citrix DaaS configuration. - Try a different client device to isolate if the problem is a client-side vs. server-side issue
Issue: Disk space full on Orka host
Symptoms:- VM deployments or image pulls fail on the host
- Host disk usage >90%
-
Check disk usage:
ansible hosts -i dev/inventory -m shell -a "df -h /opt/orka" -
List the images cached on the host:
ansible-playbook list_images.yml -i dev/inventory -e "node_name=<HOST_IP>" -
Remove old image versions from the host:
ansible-playbook delete_image.yml -i dev/inventory -e "image_name=registry.example.com/citrix-vda/sonoma-finance:v1.0" -e "node_name=<HOST_IP>" - If disk space is still low: delete VMs you no longer need and redeploy them on other hosts, expand storage, or add new host(s)
Log Locations
Orka Engine logs:
-
Location:
/opt/orka/logs/com.macstadium.orka-engine.server.managed.logon each Mac host -
View:
tail -100 /opt/orka/logs/com.macstadium.orka-engine.server.managed.log - Contains: VM lifecycle events, image operations, errors
Citrix VDA logs:
-
Location:
/Library/Application Support/Citrix/VDA/Logs/on each VM -
View: Connect to the VM with
ssh -J administrator@<HOST_IP> <VM_USERNAME>@<VM_IP>, then list the directory withls "/Library/Application Support/Citrix/VDA/Logs/"and open the log you need
System logs:
-
Location:
/var/log/system.log -
View:
sudo tail -100 /var/log/system.log - Contains: macOS system events, service crashes
Ansible logs:
- Location: Playbook output (stdout). The management UI keeps each task’s full output in its task history.
-
Verbose mode: Add
-vvvto playbook command -
Example:
ansible-playbook deploy.yml -i dev/inventory -e "vm_name=<VM_NAME>" -e "vm_image=<IMAGE>" -vvv
Centralized logging (if configured):
- Check your logging infrastructure (Splunk, ELK, CloudWatch)
- Search by hostname, VM name, or error keywords
When to Escalate to MacStadium Support
Escalate when:- The Orka Engine service crashes repeatedly
- If you are unable to deploy VMs after following the documented troubleshooting steps
- You suspect hardware failure (host unresponsive)
- Image operations fail consistently
- You need guidance on capacity planning or architecture changes
- The problem description and its symptoms/potential root causes. If you have noticed any trends or patterns when the issue occurs, provide this information as well.
- The steps you have already taken to troubleshoot the issue
- Relevant log excerpts (Orka Engine, VDA)
- Output of any diagnostic commands you have run
- Timeline of the issue (when it started, issue frequency)
- Open a ticket in the MacStadium portal, or email support@macstadium.com
- Include: Account ID, affected host IPs, error messages
- Attach: Log files, screenshots if helpful
Security and Compliance
Network Isolation Best Practices
VLAN segmentation:
Your deployment should use separate VLANs:- Management VLAN: Mac host management interfaces and the Ansible control node (restricted access)
- VM VLAN: Citrix VDA VMs (isolated from management)
- User VLAN: End-user access (if applicable)
ansible-playbook list.yml -i dev/inventory. VMs should be on a VM VLAN subnet, not a management VLAN.
Firewall rules:
Orka hosts should allow:- Inbound: SSH (port 22) from the Ansible control node only. Ansible manages Orka Engine over SSH; there’s no separate Orka Engine API port to open.
- Outbound: Container registry (port 443)
- Outbound: Internet access for macOS updates (port 80/443)
VMs should allow:
- Inbound: No inbound ports are required when you use the Rendezvous protocol, the default for non-domain-joined VDAs. The VDA opens an outbound connection to the Citrix Gateway Service.
-
Outbound: Citrix Cloud (TCP 443 to
*.xendesktop.net, and TCP/UDP 443 to*.*.nssvc.net) - Outbound: Internet access for application updates
Block unnecessary traffic:
- VM-to-VM traffic (unless required by applications)
- VM-to-management-VLAN traffic
- Direct VM-to-Internet (route connections through a company proxy if required by policy)
SSH key management
Key rotation schedule:
Rotate SSH keys annually, and/or after personnel changes.Generate a new SSH key:
Deploy the new SSH key to your host(s):
configure_semaphore.py --ssh-key-file with the new key, which replaces the SSH Key entry in Key Store that the inventory uses. The script also rewrites Base VM Credentials, so pass the same --base-vm-username and --base-vm-password you used at setup (or set BASE_VM_USERNAME and BASE_VM_PASSWORD). Confirm that playbooks still connect before you remove the old key.
Remove old SSH key(s) from a host:
Replace<OLD_KEY_COMMENT> with the comment at the end of the old public key:
SSH key storage
- Store private keys securely
- Never commit private keys to Git
- Limit access to keys (only authorized admins)
If a key is compromised
- Generate a new key immediately
- Deploy the new SSH key to all hosts
- Remove the compromised key from all hosts
- Review access logs for unauthorized use
- Document the incident for additional review
Audit logging
You will want to enable audit logging for security compliance.Citrix:
- Configuration Logging: Tracks changes to Machine Catalogs, Delivery Groups, policies
- Access: Citrix Cloud Console → Configuration → Logging
- Retention period: 90 days (configurable)
Orka:
- Orka Engine logs all VM operations
-
Location:
/opt/orka/logs/com.macstadium.orka-engine.server.managed.logon each Mac host - Retention period: Configure log rotation to match your retention policy
Ansible:
- Playbook execution logs. The management UI keeps each task’s output in its task history.
-
For command-line runs, capture stdout:
ansible-playbook ... | tee -a /var/log/ansible-playbook.log - Include timestamp(s), operator name, and the command(s) executed
What to log
- VM provisioning and deprovisioning
- User access grants and revocations
- Policy changes
- Image updates
- Configuration changes
Log review
- Weekly: Review logs for any anomalies
- Monthly: Summarize log activity for management
- Quarterly: Audit logs for compliance
Data Sovereignty Considerations
Where data resides:VM data:
-
Stored on the Orka host local disk, in
/opt/orka/data/<VM_NAME>/ - A physical location: Your data center (on-premises) or MacStadium data center (hosted)
Images:
- Stored in an OCI registry
- A physical location: Your data center (on-premises) or MacStadium data center (hosted)
User data:
- Within the VM. VMs aren’t reset between sessions, so user data stays on the VM until you delete it, for both pooled and dedicated access.
- Backup location: Your backup infrastructure
Citrix metadata:
- Stored in Citrix Cloud (US or EU region, depending on account)
- Contains: User assignments, policies, and session logs (no VM content)
Compliance considerations
GDPR (European users):
- Ensure Citrix Cloud region is EU
- Document data flows
- Implement data retention policies (delete VMs when a user leaves the organization)
HIPAA (U.S.-based healthcare policy):
- Encrypt data at rest (FileVault on VMs)
- Encrypt data in transit (HDX uses TLS)
- Implement audit logging
- Sign BAA with Citrix
Data residency requirements:
- Deploy Orka hosts in the required geographic region
- Use a container registry located in the same region
- Verify Citrix Cloud region compliance
Best practices
Image Hygiene and Update Cadence
Monthly:
- Apply macOS security updates to golden images
- Update applications (Office, browsers, etc.)
- Test the updated image thoroughly
- Deploy the image to production
Quarterly:
- Review installed applications (remove any unused applications)
- Update the Citrix VDA to the latest version
- Audit image sizes (optimize if these are >50GB)
- Document any changes made in your CHANGELOG.MD file and push these updates to Git.
Annually:
- Upgrade macOS major version (for example, Sonoma → Sequoia)
- Review all configuration scripts
- Rebuild images from scratch (vs. doing incremental updates)
Image testing checklist:
- VM deploys successfully
- The VDA registers with Citrix
- All applications launch as expected
- No errors appear in the VDA logs
- Test that user(s) can access the desktop
- Confirm session performance is acceptable
Capacity planning
Current capacity formula:
Total Capacity = (Number of Hosts) × (max_vms_per_host) = (3 hosts) × (2 VMs/host) = 6 VMsEstimate concurrent users:
Current users: 50 - Expected growth: +20 per quarter - Concurrent usage rate: 60% (30 users at peak)Calculate your VM needs:
Required VMs = (Total Users) × (Concurrent Rate) × (Buffer) = (50) × (0.6) × (1.2 buffer) = 36 VMsDetermine your host requirement(s):
Hosts Needed = (Required VMs) / (max_vms_per_host) = 36 / 2 = 18 hostsChange management procedures
Procurement timeline:- Order hosts 60 days before capacity constraint
- Allow 14 days for delivery (if hosted with MacStadium)
- Allow one week for deployment and testing
- VM utilization >75%: Start planning for host expansion
- VM utilization >90%: Expedite new hosts
Backup and Disaster Recovery
What to back up: Golden images:- Store these in an OCI registry (off-site)
- Maintain at least three recent versions
- Test the golden image VM restoration process quarterly
User data (persistent desktops only):
- Do daily backups via Time Machine or an enterprise backup tool
- 30-day retention of user data
- Test data restoration process and procedures monthly
Configuration:
- Ansible playbooks and inventory: Store these in a Git repository
- Citrix configuration: Export this monthly via PowerShell
- Documentation: Store this in a company wiki such as Confluence, in Git, or on a shared drive
Disaster recovery scenarios:
Scenario 1: Single host failure
- Impact: 2 VMs unavailable
- RTO: 4 hours
-
Procedure:
- Provision a replacement Mac
- Add the machine to your existing Ansible inventory
-
Install Orka Engine on the machine with
install_engine.yml -
Pull golden images with
pull_image.yml -
Deploy VMs to the new host from the golden image, then register them with Citrix using
register_citrix_vda.yml
Scenario 2: Complete site failure
- Impact: All VMs are unavailable
- RTO: 8-12 hours
-
Procedure:
- Provision Macs at an alternate site (or host these directly with MacStadium)
- Restore your last known good Ansible configuration from Git
-
Install Orka Engine on the new hosts with
install_engine.yml - Pull images from your OCI registry
- Deploy new VMs
- Verify VDA registration in Citrix
Disaster recovery (DR) testing:
- Annually: Conduct a full DR exercise
- Quarterly: Conduct a partial failover test
- Document lessons learned
Change Management Procedures
Before any production change:- Document the change:
- What: Description of the suggested change
- Why: Business justification for making the change
- Impact: Affected systems, users, and any anticipated downtime
- Rollback plan: Steps necessary to revert the change if any issues occur
- Test in a non-production environment:
- Deploy test VMs with their own name prefix, separate from production VMs
- Validate functionality
- Measure performance impact
- Schedule a change maintenance window:
- Communicate the change to users 1 week in advance
- Choose a low-usage time (evenings, weekends)
- Change duration: 2× expected time (buffer for issues)
- Execute the change:
- Follow the documented change procedure
- Monitor for any issues
- Validate success criteria has been met
- Communicate change completion
- Post-change review process:
- Document the actual vs. planned change(s)
- Capture any lessons learned during the change process
- Update change procedure(s) if needed
Example change: Update golden image from v1.2 to v2.0
Change request:
- Change: Update finance golden image to v2.0 (adds Microsoft Teams)
- Impact: 50 finance VMs, 200 users
- Downtime: 2 hours (pooled desktops, off-hours)
-
Rollback: Revert
vm_imageto v1.2, redeploy VMs
Test plan:
- Deploy 2 test VMs from v2.0 image
- Validate Microsoft Teams launches correctly
- Test user session quality is acceptable
Change execution plan:
-
Update
vm_imagein group vars to v2.0 -
Delete any existing VMs:
delete.yml(30 min) -
Deploy the new VMs:
deploy.yml(60 min) -
Register the new VMs with Citrix using
register_citrix_vda.yml, and verify VDA registration in Citrix (15 min) - Test user sessions (15 min)
Change rollback plan (if adverse issues occur):
-
Update the
vm_imageback to v1.2 - Delete v2.0 VMs
- Redeploy VMs from v1.2 and register them with Citrix
Change communication:
- 1 week before scheduled change: “Scheduled maintenance Sunday 2 AM-4 AM, desktops will be unavailable”
- Day of change: “Maintenance is currently in progress, desktops will be available by 4 AM”
- After change is complete: “Maintenance is now complete, Microsoft Teams is now available on finance desktops.”
Resources and support
Documentation:
MacStadium
- Orka Documentation: docs.macstadium.com
- Bare Metal Mac Options: Cloud-hosted Bare Metal Macs | MacStadium
Citrix
- VDA for macOS Documentation: Citrix Virtual Delivery Agent for macOS
- System Requirements: System Requirements | Citrix Virtual Delivery Agent for macOS
- Citrix Cloud Console: https://citrix.cloud.com
- Citrix Support: Citrix Support
Ansible:
- Ansible Documentation: Ansible Community | Ansible documentation
- Orchestration playbook reference: Ansible quick reference
Support Contact Information
MacStadium Support:
- Portal: portal.macstadium.com (open a support ticket)
- Email: support@macstadium.com
- Phone support, support hours, and response times depend on your support plan. See MacStadium support tiers.
- Your Account ID
- Affected host IPs or VM names
- Problem description and timeline
- The troubleshooting steps you have already taken
- Log excerpts (Orka Engine, VDA)
Citrix Support:
- Portal: Citrix Support
- Access via Citrix Cloud Console → Support
- Include: Your Customer ID, Machine Catalog name, applicable error messages
Citrix community forums:
- Citrix Discussions: https://discussions.citrix.com
- Citrix Community: Citrix Community

