Deploying Neko - Self-Hosted Virtual Browser Platform on Linux
DEV Community

Deploying Neko - Self-Hosted Virtual Browser Platform on Linux

Set Up the Directory Structure

  1. Create the project directory with a Firefox profile subdirectory:
$ mkdir -p ~/neko/profile
$ cd ~/neko
  1. Create the environment file:
$ nano .env
DOMAIN=neko.example.com
LETSENCRYPT_EMAIL=admin@example.com
NEKO_ADMIN_PASSWORD=StrongAdminPassword123
NEKO_USER_PASSWORD=StrongUserPassword456
  1. Set ownership on the profile directory - Neko runs as UID 1000:
$ sudo chown -R 1000:1000 ~/neko/profile

Deploy with Docker Compose

  1. Create the Docker Compose manifest:
$ nano docker-compose.yaml
services:
  traefik:
    image: traefik:v3.6
    container_name: traefik
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entrypoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
      - "--certificatesresolvers.letsencrypt.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    restart: unless-stopped

  neko:
    image: ghcr.io/m1k1o/neko/firefox:3
    container_name: neko
    shm_size: "2gb"
    expose:
      - "8080"
    ports:
      - "52000-52100:52000-52100/udp"
    volumes:
      - "./profile:/home/neko/.mozilla/firefox/profile.default"
    environment:
      NEKO_MEMBER_PROVIDER: "multiuser"
      NEKO_MEMBER_MULTIUSER_USER_PASSWORD: "${NEKO_USER_PASSWORD}"
      NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "${NEKO_ADMIN_PASSWORD}"
      NEKO_SERVER_PROXY: "true"
      NEKO_DESKTOP_SCREEN: "1280x720@30"
      NEKO_WEBRTC_EPR: "52000-52100"
      NEKO_WEBRTC_ICELITE: "true"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.neko.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.neko.entrypoints=websecure"
      - "traefik.http.routers.neko.tls.certresolver=letsencrypt"
      - "traefik.http.services.neko.loadbalancer.server.port=8080"
    restart: unless-stopped

volumes:
  letsencrypt:

The shm_size: "2gb" allocation is required - Firefox rendering crashes or freezes without enough shared memory. NEKO_WEBRTC_EPR opens the UDP port range WebRTC media travels over; NEKO_WEBRTC_ICELITE suits servers with a public IP and direct inbound UDP.

  1. Start the services:
$ docker compose up -d
  1. Verify both services are running:
$ docker compose ps
$ docker compose logs

Access Neko

  • Open https://neko.example.com.
  • Enter a display name and the admin password from .env, then click CONNECT.
  • The Firefox window loads in the center with a control bar below. Connected users appear as avatars; a green badge marks whoever holds control.
  • The side panel has Chat (activity log) and Settings (scroll sensitivity, keyboard layout, autoplay).

Run a Collaborative Session

  • Share the URL and the user password (not the admin one) with participants.
  • Each participant enters a display name + password and clicks CONNECT - their avatar appears at the bottom.
  • Navigate to any URL in the address bar. WebRTC streams it to everyone with minimal delay.
  • Right-click a user's avatar to Give Controls, Kick, or Ban IP (kick/ban are admin-only).

Next Steps

Neko is running and streaming a shared browser session over HTTPS. From here you can:

  • Swap the Firefox image for Chromium or a custom browser image
  • Add a TURN server for participants behind restrictive NATs
  • Scale to multiple rooms by running additional Neko containers on different subdomains

For the full guide with additional tips, visit the original article on Vultr Docs.

Comments

No comments yet. Start the discussion.