# Desktop (/docs/template/examples/desktop)

<!-- agent-signals: reading_time_min: 3 · est_tokens: 1933 · updated: 2026-07-30 -->
Related: [Next.js app](/docs/template/examples/nextjs.md), [Next.js app (Bun)](/docs/template/examples/nextjs-bun.md), [Expo app](/docs/template/examples/expo.md), [Claude Code](/docs/template/examples/claude-code.md), [Docker](/docs/template/examples/docker.md)

This template creates a sandbox with a full Ubuntu 22.04 desktop environment, including the XFCE desktop, common applications, and VNC streaming for remote access. It's ideal for building AI agents that need to interact with graphical user interfaces.

The template includes:

* **Ubuntu 22.04** with XFCE desktop environment
* **VNC streaming** via [noVNC](https://novnc.com/) for browser-based access
* **Pre-installed applications**: LibreOffice, text editors, file manager, and common utilities
* **Automation tools**: [xdotool](https://github.com/jordansissel/xdotool) and [scrot](https://github.com/resurrecting-open-source-projects/scrot) for programmatic desktop control

## Template definition [#template-definition]

The template installs the desktop environment, sets up VNC streaming via [x11vnc](https://github.com/LibVNC/x11vnc) and noVNC, and configures a startup script.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript expandable  
      // template.ts
      import { Template, waitForPort } from 'e2b'

      export const template = Template()
        .fromUbuntuImage('22.04')
        // Desktop environment and system utilities
        .runCmd([
          'yes | unminimize',
          'apt-get update',
          'apt-get install -y \
            xserver-xorg \
            xorg \
            x11-xserver-utils \
            xvfb \
            x11-utils \
            xauth \
            xfce4 \
            xfce4-goodies \
            util-linux \
            sudo \
            curl \
            git \
            wget \
            python3-pip \
            xdotool \
            scrot \
            ffmpeg \
            x11vnc \
            net-tools \
            netcat \
            x11-apps \
            libreoffice \
            xpdf \
            gedit \
            xpaint \
            tint2 \
            galculator \
            pcmanfm',
          'apt-get clean',
          'rm -rf /var/lib/apt/lists/*',
        ])
        // Streaming server setup
        .runCmd([
          'git clone --branch e2b-desktop https://github.com/e2b-dev/noVNC.git /opt/noVNC',
          'ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html',
          'git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify',
        ])
        // Set default terminal
        .runCmd(
          'ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator'
        )
        .copy('start_command.sh', '/start_command.sh')
        .runCmd('chmod +x /start_command.sh')
        .setStartCmd('/start_command.sh', waitForPort(6080))

      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python expandable  
      # template.py
      from e2b import Template, wait_for_port

      template = (
          Template()
          .from_image("ubuntu:22.04")
          # Initial system setup and packages
          # We are not using .apt_install() here because some packages have interactive prompts (keyboard layout setup, etc.)
          .run_cmd(
              [
                  "yes | unminimize",
                  "apt-get update",
                  "apt-get install -y \
                      xserver-xorg \
                      xorg \
                      x11-xserver-utils \
                      xvfb \
                      x11-utils \
                      xauth \
                      xfce4 \
                      xfce4-goodies \
                      util-linux \
                      sudo \
                      curl \
                      git \
                      wget \
                      python3-pip \
                      xdotool \
                      scrot \
                      ffmpeg \
                      x11vnc \
                      net-tools \
                      netcat \
                      x11-apps \
                      libreoffice \
                      xpdf \
                      gedit \
                      xpaint \
                      tint2 \
                      galculator \
                      pcmanfm",
                  "apt-get clean",
                  "rm -rf /var/lib/apt/lists/*",
              ]
          )
          # Setup NoVNC and websockify
          .run_cmd(
              [
                  "git clone --branch e2b-desktop https://github.com/e2b-dev/noVNC.git /opt/noVNC",
                  "ln -s /opt/noVNC/vnc.html /opt/noVNC/index.html",
                  "git clone --branch v0.12.0 https://github.com/novnc/websockify /opt/noVNC/utils/websockify",
              ]
          )
          # Set default terminal
          .run_cmd(
              "ln -sf /usr/bin/xfce4-terminal.wrapper /etc/alternatives/x-terminal-emulator"
          )
          # Copy the start command
          .copy("start_command.sh", "/start_command.sh")
          .run_cmd("chmod +x /start_command.sh")
          # Set start command to launch the desktop environment
          .set_start_cmd("/start_command.sh", wait_for_port(6080))
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

## Startup script [#startup-script]

The startup script initializes the virtual display using [Xvfb](https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml) (X Virtual Framebuffer), launches the XFCE desktop session, starts the VNC server, and exposes the desktop via noVNC on port 6080. This script runs automatically when the sandbox starts.

```bash title="start_command.sh"
#!/bin/bash

# Set display
export DISPLAY=${DISPLAY:-:0}

# Start Xvfb
Xvfb $DISPLAY -ac -screen 0 1024x768x24 -nolisten tcp &
sleep 2

# Start XFCE session
startxfce4 &
sleep 5

# Start VNC server
x11vnc -bg -display $DISPLAY -forever -wait 50 -shared -rfbport 5900 -nopw \
    -noxdamage -noxfixes -nowf -noscr -ping 1 -repeat -speeds lan &
sleep 2

# Start noVNC server
cd /opt/noVNC/utils && ./novnc_proxy --vnc localhost:5900 --listen 6080 --web /opt/noVNC --heartbeat 30 &
sleep 2
```

## Building the template [#building-the-template]

Build the template with increased CPU and memory allocation to handle the desktop environment installation. The build process may take several minutes due to the size of the packages being installed.

<CodeGroup>
  <CodeBlockTabs defaultValue="JavaScript & TypeScript" groupId="javascript-typescript+python">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="JavaScript & TypeScript">
        JavaScript & TypeScript
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Python">
        Python
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="JavaScript & TypeScript">
      ```typescript  
      // build.ts
      import { Template, defaultBuildLogger } from 'e2b'
      import { template as desktopTemplate } from './template'

      await Template.build(desktopTemplate, 'desktop', {
        cpuCount: 8,
        memoryMB: 8192,
        onBuildLogs: defaultBuildLogger(),
      })
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Python">
      ```python  
      # build.py
      from e2b import Template, default_build_logger
      from .template import template as desktopTemplate

      Template.build(desktopTemplate, 'desktop',
          cpu_count=8,
          memory_mb=8192,
          on_build_logs=default_build_logger(),
      )
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>
