Backend & Runtime

    Deploy Deno Apps on a VPS

    Install the secure TypeScript runtime, run a production HTTP server with systemd, and put Nginx + SSL in front — all on a $4/month VPS.

    Prerequisites

    • A RamNode VPS running Ubuntu 22.04 or 24.04 (any plan works; even the $4/month KVM SSD plan is sufficient)
    • Root or sudo access over SSH
    • A domain name pointed at your VPS IP (optional but recommended for SSL)
    1

    Update Your System

    Start with a clean package state:

    Update packages
    sudo apt update && sudo apt upgrade -y
    2

    Install Deno

    Deno ships as a single binary. The official installer handles everything:

    Install Deno
    curl -fsSL https://deno.land/install.sh | sh

    Add it to your PATH:

    Add to PATH
    echo 'export DENO_INSTALL="$HOME/.deno"' >> ~/.bashrc
    echo 'export PATH="$DENO_INSTALL/bin:$PATH"' >> ~/.bashrc
    source ~/.bashrc

    Verify the installation:

    Verify
    deno --version

    You should see output like deno 2.x.x with v8 and TypeScript versions.

    3

    Create Your Application

    Create a directory for your app and a simple HTTP server to test with:

    Create app directory
    mkdir -p /var/www/myapp
    cd /var/www/myapp

    Create main.ts:

    main.ts
    const port = parseInt(Deno.env.get("PORT") ?? "8000");
    
    const handler = (_req: Request): Response => {
      return new Response("Hello from Deno on RamNode!", {
        status: 200,
        headers: { "Content-Type": "text/plain" },
      });
    };
    
    console.log(`Listening on http://localhost:${port}`);
    Deno.serve({ port }, handler);

    Test it manually:

    Run test
    deno run --allow-net --allow-env main.ts

    In a second terminal, verify the response:

    Check response
    curl http://localhost:8000

    You should see Hello from Deno on RamNode!. Hit Ctrl+C to stop.

    4

    Create a Dedicated System User

    Running your app as root is a security risk. Create a low-privilege user instead:

    Create service user
    sudo useradd --system --no-create-home --shell /usr/sbin/nologin denoapp
    sudo chown -R denoapp:denoapp /var/www/myapp

    Copy the Deno binary to a system-wide location:

    System-wide binary
    sudo cp ~/.deno/bin/deno /usr/local/bin/deno
    sudo chmod +x /usr/local/bin/deno
    5

    Configure a systemd Service

    Create a service file so your app starts automatically on boot and restarts on failure:

    /etc/systemd/system/denoapp.service
    [Unit]
    Description=Deno App
    After=network.target
    
    [Service]
    Type=simple
    User=denoapp
    Group=denoapp
    WorkingDirectory=/var/www/myapp
    ExecStart=/usr/local/bin/deno run \
      --allow-net \
      --allow-env \
      --allow-read=/var/www/myapp \
      main.ts
    Restart=on-failure
    RestartSec=5
    StandardOutput=journal
    StandardError=journal
    Environment=PORT=8000
    Environment=DENO_DIR=/var/www/myapp/.deno_cache
    
    [Install]
    WantedBy=multi-user.target
    • --allow-net, --allow-env, --allow-read follow Deno's explicit permission model — only grant what your app needs.
    • DENO_DIR sets the module cache to a path the service user can write to.
    • Restart=on-failure with RestartSec=5 prevents rapid crash loops.

    Create the cache directory and enable the service:

    Setup and start
    sudo mkdir -p /var/www/myapp/.deno_cache
    sudo chown -R denoapp:denoapp /var/www/myapp/.deno_cache
    sudo systemctl daemon-reload
    sudo systemctl enable denoapp
    sudo systemctl start denoapp

    Check status and follow logs:

    Verify
    sudo systemctl status denoapp
    sudo journalctl -u denoapp -f
    6

    Install and Configure Nginx

    Putting Nginx in front gives you SSL termination, static file serving, gzip compression, and rate limiting without changing your app code.

    Install Nginx
    sudo apt install -y nginx

    Create a site config:

    /etc/nginx/sites-available/myapp
    server {
        listen 80;
        server_name yourdomain.com www.yourdomain.com;
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_cache_bypass $http_upgrade;
        }
    }

    Enable the site and test:

    Enable and reload
    sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx
    7

    Add SSL with Let's Encrypt

    Install Certbot
    sudo apt install -y certbot python3-certbot-nginx

    Request a certificate:

    Get certificate
    sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

    Verify auto-renewal:

    Test renewal
    sudo certbot renew --dry-run
    8

    Open Firewall Ports

    If you are using UFW:

    Allow traffic
    sudo ufw allow 'Nginx Full'
    sudo ufw enable

    This allows ports 80 and 443. Your Deno app on port 8000 stays internal and is not exposed directly.

    9

    Pre-cache Dependencies (Optional)

    On first startup, Deno fetches and caches remote imports. To avoid this delay at runtime, pre-cache during deployment:

    Pre-cache modules
    sudo -u denoapp DENO_DIR=/var/www/myapp/.deno_cache deno cache main.ts

    For apps using an import map or lock file:

    With import map
    sudo -u denoapp DENO_DIR=/var/www/myapp/.deno_cache deno cache --import-map=import_map.json main.ts
    10

    Deploying Updates

    For a simple update workflow without a full CI pipeline:

    Deploy updates
    cd /var/www/myapp
    git pull origin main
    sudo -u denoapp DENO_DIR=/var/www/myapp/.deno_cache deno cache main.ts
    sudo systemctl restart denoapp
    sudo systemctl status denoapp

    Troubleshooting

    Service fails to start

    Check the logs first:

    View logs
    sudo journalctl -u denoapp -n 50 --no-pager

    Common causes: missing permission flags, wrong WorkingDirectory, or a port already in use.

    Permission denied errors

    Deno's sandbox throws permission errors if your app accesses something not covered by the flags in your service file. Add the required flag (--allow-read, --allow-write, etc.) and restart.

    Module download failures on first run

    The denoapp user needs write access to DENO_DIR. Double-check ownership:

    Fix ownership
    sudo chown -R denoapp:denoapp /var/www/myapp/.deno_cache

    502 Bad Gateway from Nginx

    This usually means your Deno process is not listening on the expected port. Confirm with:

    Check port
    sudo ss -tlnp | grep 8000

    Next Steps

    With your Deno app running behind Nginx on a RamNode VPS you have a production-ready setup. From here you might consider:

    • Adding a GitHub Actions workflow to automate deployments via SSH on push
    • Using Deno's built-in Deno.serve with WebSockets for real-time features
    • Mounting a RamNode block storage volume for persistent file uploads
    • Running multiple Deno apps on the same VPS using different ports and Nginx server_name blocks
    • Adding monitoring with something lightweight like Netdata or a simple uptime check