{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Linux Server Deployment","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"linux-server-deployment","__idx":0},"children":["Linux Server Deployment"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Deploy MoreLogin on a headless Ubuntu server and automate browser profiles via the Local API — no desktop environment required."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-youll-achieve","__idx":1},"children":["What You'll Achieve"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By the end of this guide you will have:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A running MoreLogin instance on an Ubuntu 24.04 headless server"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Network forwarding configured so external machines can connect via CDP (Chrome DevTools Protocol)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A working Python automation script that creates, launches, controls, and cleans up browser profiles"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"architecture-overview","__idx":2},"children":["Architecture Overview"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"┌──────────────────────────────────────────────────────────┐\n│                    Ubuntu 24.04 Server                   │\n│                                                          │\n│  ┌──────────────┐    ┌───────────────────────────────┐   │\n│  │   xvfb       │───▶│  MoreLogin AppImage           │   │\n│  │ (virtual     │    │  Local API :40000             │   │\n│  │  display)    │    │  CDP debug :<dynamic>         │   │\n│  │              │    │    (127.0.0.1, per profile)   │   │\n│  └──────────────┘    └───────────────────────────────┘   │\n│                              │                           │\n│                        socat forwarding                  │\n│                              │                           │\n│                     0.0.0.0:40001 → 127.0.0.1:40000      │\n│                     0.0.0.0:<N+1> → 127.0.0.1:<N>        │\n└──────────────────────────────────────────────────────────┘\n                               │\n                          External machine\n                     (Playwright / Puppeteer / Selenium)\n"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!NOTE]"," ","The CDP debug port is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["dynamic"]}," — each browser profile gets its own port, returned by the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api/env/start"]}," endpoint. The diagram above uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["<N>"]}," as a placeholder."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":3},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Requirement"},"children":["Requirement"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Details"},"children":["Details"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Operating System"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Ubuntu 24.04 Server (x86_64)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Recommended Specs"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["8 vCPU, 8 GB RAM (supports ~5 concurrent profiles)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Network"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Outbound internet access; open inbound port ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["40001"]}," and the forwarded CDP ports you choose, or use SSH tunnels instead"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Python"]}," (optional)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Python 3.8+ with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pip"]}," for running the example script"]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1--install-system-dependencies","__idx":4},"children":["Step 1 — Install System Dependencies"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Connect to your server via SSH and install the required packages:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# FUSE support (required for AppImage)\nsudo apt install -y libfuse2t64\n\n# GTK / accessibility / display libraries\nsudo apt install -y libatk1.0-0 libatk-bridge2.0-0 libatspi2.0-0\nsudo apt install -y libcups2\nsudo apt install -y libgtk-3-0 libgdk-pixbuf2.0-0\nsudo apt install -y libgbm1 libxkbcommon0 libasound2t64\n\n# Virtual framebuffer (headless display)\nsudo apt install -y xvfb\n\n# TCP forwarder\nsudo apt install -y socat\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"install-fonts-optional","__idx":5},"children":["Install Fonts (Optional)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If browser pages render without text or show missing characters, install the corresponding font packages:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# CJK (Chinese, Japanese, Korean)\nsudo apt install -y fonts-noto-cjk fonts-noto-cjk-extra\n\n# Arabic\nsudo apt install -y fonts-noto-color-emoji fonts-noto-extra\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For other languages, install the matching ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://fonts.google.com/noto"},"children":["Noto font family"]}," package."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2--download--launch-morelogin","__idx":6},"children":["Step 2 — Download & Launch MoreLogin"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"21-download-the-appimage","__idx":7},"children":["2.1 Download the AppImage"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"wget https://get.morelogin.com/client/prod/linux/x64/2.54.0/MoreLogin_x86_64_2.54.0.AppImage\nchmod +x MoreLogin_x86_64_2.54.0.AppImage\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Replace ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2.54.0"]}," with the latest version available from your MoreLogin account or the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.morelogin.com/download/"},"children":["download page"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"22-start-morelogin-in-the-background","__idx":8},"children":["2.2 Start MoreLogin in the Background"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["xvfb-run"]}," to provide a virtual display, then run the AppImage:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"nohup xvfb-run -a ./MoreLogin_x86_64_2.54.0.AppImage --no-sandbox > morelogin.log 2>&1 &\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Verify it started successfully:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Check the process is running\nps aux | grep MoreLogin\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The process may take 5–10 seconds to fully initialize."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3--log-in-to-morelogin-via-api-required","__idx":9},"children":["Step 3 — Log In to MoreLogin via API (Required)"]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!CAUTION]"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["You must log in before calling any other Local API endpoint."]}," On a headless server there is no GUI to log in manually, so you must authenticate via the API. Without this step, all API calls will return:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"status\": \"error\", \"code\": 401, \"message\": \"Your login status has expired, please log in again\"}\n","lang":"json"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"31-get-your-api-credentials","__idx":10},"children":["3.1 Get Your API Credentials"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Open the MoreLogin desktop client (on any machine where you're logged in) and navigate to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Settings → API & MCP"]},". Copy the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["APP ID"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["API Key"]}," from the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Open API"]}," section:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/image.cfac9a4c31ae47e8434739e42cab4601ee08d737bba4fccba40f2f3bc78e9648.4b463470.png","alt":"MoreLogin API Settings — Copy APP ID and API Key from the Open API section"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"32-log-in-via-curl","__idx":11},"children":["3.2 Log In via curl"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Call the login endpoint with your credentials:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST http://127.0.0.1:40000/api/user/login \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"apiId\": \"YOUR_APP_ID\",\n    \"apiKey\": \"YOUR_API_KEY\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A successful response looks like:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"code\": 0, \"msg\": null, \"data\": true}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"33-verify-the-login","__idx":12},"children":["3.3 Verify the Login"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Confirm the API is ready by listing browser profiles:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -s -X POST http://127.0.0.1:40000/api/env/page \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"pageNo\": 1, \"pageSize\": 1}'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"code\":0, ...}"]}," response means you are logged in and the API is ready."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Checkpoint:"]}," Your MoreLogin server is fully operational. Proceed to ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-4--configure-network-forwarding-socat"},"children":["Step 4"]}," for remote access, or jump directly to ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-5--quick-automation-example"},"children":["Step 5"]}," if running scripts on the same server."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!NOTE]"," ","The login session persists as long as the MoreLogin process is running. If you restart the AppImage, you will need to log in again."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-4--configure-network-forwarding-socat","__idx":13},"children":["Step 4 — Configure Network Forwarding (socat)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, both the Local API (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[":40000"]},") and CDP debug ports bind to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["127.0.0.1"]},". If you need to access them from an external machine (e.g., your development laptop), use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["socat"]}," to forward traffic."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!WARNING]"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Security risk — do not expose these ports to the public internet."]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The Local API has ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no built-in authentication"]}," for most endpoints."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A CDP debug port grants ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["full remote control"]}," of the browser instance (read cookies, inject scripts, capture screenshots)."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Recommendations:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["SSH tunnel"]}," instead of socat for remote access: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ssh -L 40000:127.0.0.1:40000 user@server"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you must use socat, restrict access with firewall rules to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["specific IPs only"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["VPN"]}," or cloud provider security groups to limit inbound traffic"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Never"]}," open ports ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["40001"]}," / CDP ports to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0.0.0.0"]}," on a public-facing server without IP restrictions"]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"41-forward-the-local-api-port","__idx":14},"children":["4.1 Forward the Local API Port"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"nohup socat TCP-LISTEN:40001,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:40000 &\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["External machines can now reach the API at ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http://<server-ip>:40001"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"42-forward-cdp-debug-ports","__idx":15},"children":["4.2 Forward CDP Debug Ports"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you start a browser profile via the API, the response includes a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["dynamic"]}," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debugPort"]}," (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["9222"]},"). Each profile may receive a different port. Forward it so external automation tools (Playwright, Puppeteer, Selenium) can connect:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Example: if debugPort=9222, forward to external port 9223 (debugPort + 1)\n# Adjust both ports to match the actual debugPort returned by /api/env/start\nnohup socat TCP-LISTEN:9223,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:9222 &\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!WARNING]"," ","When running multiple profiles concurrently, ensure forwarded ports do not collide with other profiles' debug ports. For example, if profile A gets ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debugPort=9222"]}," and you forward to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["9223"]},", but profile B receives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debugPort=9223"]},", there will be a port conflict. Consider using a larger offset or a dedicated port range."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!TIP]"," ","In production, create socat forwarding ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["dynamically"]}," after each ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api/env/start"]}," call, using the returned ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debugPort"]},". See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#52-create--start--automate--cleanup"},"children":["Python example"]}," for a working implementation."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If running automation scripts ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["on the same server"]},", you can skip socat and connect directly to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["127.0.0.1"]},"."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"43-open-firewall-ports","__idx":16},"children":["4.3 Open Firewall Ports"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you use socat, restrict access to trusted IPs only:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Allow only a specific IP (recommended)\nsudo ufw allow from <YOUR_IP> to any port 40001 proto tcp\nsudo ufw allow from <YOUR_IP> to any port 9223 proto tcp\n\n# Or allow from any IP (NOT recommended for production)\n# sudo ufw allow 40001/tcp\n# sudo ufw allow 9223/tcp\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"44-recommended-use-ssh-tunnel-instead","__idx":17},"children":["4.4 Recommended: Use SSH Tunnel Instead"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For the most secure remote access, use an SSH tunnel — no firewall changes or socat needed."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because the CDP port is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["dynamic"]}," (assigned when you start a profile), the workflow is:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Tunnel the API port first:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Run this on your local machine\nssh -L 40000:127.0.0.1:40000 user@<server-ip>\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Start the profile"]}," via the tunneled API (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http://127.0.0.1:40000/api/env/start"]},") and read the returned ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["debugPort"]},"."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Open a second tunnel for the CDP port:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Replace <debugPort> with the actual port returned by the API\nssh -L <debugPort>:127.0.0.1:<debugPort> user@<server-ip>\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Connect"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http://127.0.0.1:<debugPort>"]}," from your local Playwright / Puppeteer scripts as if the server were local."]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!TIP]"," ","You can combine both tunnels in one command if you know the port range in advance, e.g.:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"ssh -L 40000:127.0.0.1:40000 -L 9222:127.0.0.1:9222 -L 9223:127.0.0.1:9223 user@<server-ip>\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["But in practice it's easier to start the API tunnel first, then add per-profile tunnels as needed."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-5--quick-automation-example","__idx":18},"children":["Step 5 — Quick Automation Example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Below is a minimal Python example showing the full lifecycle of a browser profile. For the complete production-ready script with concurrency and error handling, see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/MoreLoginBrowser/MoreLogin-API-Demos/blob/main/MoreLogin-Python/linux_server_test.py"},"children":["full example on GitHub"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"51-install-python-dependencies","__idx":19},"children":["5.1 Install Python Dependencies"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"pip install requests playwright\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Playwright is used here only for its CDP client (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connect_over_cdp"]},"). You do ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," need to run ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["playwright install"]}," — MoreLogin provides its own browser."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"52-create--start--automate--cleanup","__idx":20},"children":["5.2 Create → Start → Automate → Cleanup"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This example assumes the script runs ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["on the same server"]}," as MoreLogin. For remote scenarios, see the notes after the code."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import requests\nfrom playwright.sync_api import sync_playwright\n\n# ── Configuration ──────────────────────────────────────────\n# Local:  script runs on the SAME server as MoreLogin\n# Remote: script runs on a DIFFERENT machine — see notes below\nAPI_BASE  = \"http://127.0.0.1:40000\"\n\n\n# ① Create a browser profile\nresp = requests.post(f\"{API_BASE}/api/env/create/quick\", json={\n    \"browserTypeId\": 1,\n    \"operatorSystemId\": 1,\n    \"quantity\": 1\n})\nresp_data = resp.json()\nassert resp_data[\"code\"] == 0, f\"Create failed: {resp_data}\"\nenv_id = resp_data[\"data\"][\"envIds\"][0]\nprint(f\"✅ Created profile: {env_id}\")\n\n\n# ② Start the profile (headless)\nresp = requests.post(f\"{API_BASE}/api/env/start\", json={\n    \"envId\": env_id\n})\nresp_data = resp.json()\nassert resp_data[\"code\"] == 0, f\"Start failed: {resp_data}\"\ndebug_port = resp_data[\"data\"][\"debugPort\"]   # Dynamic — different for each profile\nprint(f\"✅ Started — debug port: {debug_port}\")\n\n\n# ③ Build the CDP URL\ncdp_url = f\"http://127.0.0.1:{debug_port}\"\n\n\n# ④ Connect via CDP and automate\nwith sync_playwright() as p:\n    browser = p.chromium.connect_over_cdp(cdp_url)\n    page = browser.contexts[0].pages[0]\n    page.goto(\"https://www.google.com\")\n    page.screenshot(path=f\"screenshot_{env_id}.png\")\n    print(f\"✅ Screenshot saved\")\n    # Use disconnect() — not close() — to detach without killing the browser.\n    # The profile will be stopped cleanly via the API in step ⑤.\n    browser.disconnect()\n\n\n# ⑤ Stop the profile\nrequests.post(f\"{API_BASE}/api/env/close\", json={\"envId\": env_id})\nprint(f\"✅ Profile stopped\")\n\n\n# ⑥ Delete the profile\nrequests.post(f\"{API_BASE}/api/env/remove\", json={\"envIds\": [env_id]})\nprint(f\"✅ Profile deleted\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!NOTE]"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Running from a remote machine?"]}," Two approaches:"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Option A — SSH tunnel (recommended):"]}," ","Set up SSH tunnels from your local machine to the server (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#44-recommended-use-ssh-tunnel-instead"},"children":["§ 4.4"]},"), then keep ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["API_BASE = \"http://127.0.0.1:40000\""]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cdp_url = f\"http://127.0.0.1:{debug_port}\""]}," — SSH makes the remote ports appear local."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Option B — socat on the server:"]}," ","On the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["server"]},", start socat forwarding for the API port and for each CDP port:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Run these on the SERVER, not on your local machine\nsocat TCP-LISTEN:40001,fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:40000 &\nsocat TCP-LISTEN:$((debug_port+1)),fork,reuseaddr,bind=0.0.0.0 TCP:127.0.0.1:$debug_port &\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Then in your script, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["API_BASE = \"http://<server-ip>:40001\""]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["cdp_url = f\"http://<server-ip>:{debug_port + 1}\""]},"."," ","⚠️ Restrict access with firewall rules — see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#43-open-firewall-ports"},"children":["§ 4.3"]},"."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!IMPORTANT]"," ","The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["resp.json()[\"data\"][\"envIds\"]"]}," structure matches the current ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api/env/create/quick"]}," response format. If you encounter a different shape (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data: [\"id1\", ...]"]},"), check the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/browser"},"children":["API Reference"]}," for your version — response formats may vary across releases."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"performance-benchmark","__idx":21},"children":["Performance Benchmark"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following results were obtained on an Ubuntu 24.04 Server VM (8 vCPU, 8 GB RAM) using the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/MoreLoginBrowser/MoreLogin-API-Demos/blob/main/MoreLogin-Python/linux_server_test.py"},"children":["full stress test script"]},":"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Metric"},"children":["Metric"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Value"},"children":["Value"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Total runs"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["100"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Concurrency"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4 (simultaneous)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Success rate"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["100.0%"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Total time"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["604.06 s"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Avg time per task"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6.04 s"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Throughput"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["0.17 tasks/s"]}]}]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These numbers serve as a baseline. Actual performance depends on server specs, network conditions, and page complexity."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"production-deployment-systemd","__idx":22},"children":["Production Deployment (systemd)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For production servers, run MoreLogin as a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["systemd service"]}," to get automatic startup on boot, restart on crash, and centralized logging."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"create-the-service-file","__idx":23},"children":["Create the Service File"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"sudo tee /etc/systemd/system/morelogin.service > /dev/null <<'EOF'\n[Unit]\nDescription=MoreLogin Browser (headless)\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nUser=root\nWorkingDirectory=/opt/morelogin\nExecStart=/usr/bin/xvfb-run -a /opt/morelogin/MoreLogin_x86_64_2.54.0.AppImage --no-sandbox\nRestart=on-failure\nRestartSec=10\nStandardOutput=journal\nStandardError=journal\n\n[Install]\nWantedBy=multi-user.target\nEOF\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Adjust ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["WorkingDirectory"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ExecStart"]}," paths to match where you placed the AppImage."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!TIP]"," ","For production, consider creating a dedicated user (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["morelogin"]},") instead of running as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["root"]},", and adjust file ownership and permissions accordingly. If the AppImage currently requires root privileges, you may keep ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["User=root"]},", but isolating the process under a non-root account is a best practice."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"enable-and-start","__idx":24},"children":["Enable and Start"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"sudo systemctl daemon-reload\nsudo systemctl enable morelogin    # Auto-start on boot\nsudo systemctl start morelogin     # Start now\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"manage-the-service","__idx":25},"children":["Manage the Service"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"sudo systemctl status morelogin    # Check status\nsudo journalctl -u morelogin -f    # Stream logs\nsudo systemctl restart morelogin   # Restart\nsudo systemctl stop morelogin      # Stop\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["[!NOTE]"," ","After a restart, you must call the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#32-log-in-via-curl"},"children":["login endpoint"]}," again — the API session does not survive process restarts. For automated recovery, consider adding an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ExecStartPost"]}," script or a health-check cron job that calls the login endpoint after the service starts."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":26},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"morelogin-fails-to-start","__idx":27},"children":["MoreLogin fails to start"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"AppImages require FUSE to run.\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix:"]}," Install FUSE support:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"sudo apt install -y libfuse2t64\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"browser-pages-show-blank-text--missing-characters","__idx":28},"children":["Browser pages show blank text / missing characters"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix:"]}," Install the font package for the target language (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#install-fonts-optional"},"children":["Step 1 — Install Fonts"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"cdp-connection-refused-from-external-machine","__idx":29},"children":["CDP connection refused from external machine"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cause:"]}," CDP debug ports bind to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["127.0.0.1"]}," by default."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix:"]}," Set up socat forwarding (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-4--configure-network-forwarding-socat"},"children":["Step 4"]},") and ensure firewall rules allow the forwarded port."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"curl-to-local-api-returns-connection-refused","__idx":30},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["curl"]}," to Local API returns \"Connection refused\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cause:"]}," MoreLogin hasn't finished starting yet, or the process crashed."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Wait 5–10 seconds after startup"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Check ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["morelogin.log"]}," for errors"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Verify the process is running: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ps aux | grep MoreLogin"]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"api-returns-401-login-status-has-expired","__idx":31},"children":["API returns 401 \"login status has expired\""]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\"status\": \"error\", \"code\": 401, \"message\": \"Your login status has expired, please log in again\"}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Cause:"]}," You haven't logged in via the API, or the MoreLogin process was restarted."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fix:"]}," Call the login endpoint before any other API call (see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#step-3--log-in-to-morelogin-via-api-required"},"children":["Step 3"]},")."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"next-steps","__idx":32},"children":["Next Steps"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Goal"},"children":["Goal"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Link"},"children":["Link"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Full Browser API reference"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/browser"},"children":["Browser API"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Authentication setup"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/getting-started/authentication"},"children":["Authentication Guide"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Playwright / Selenium / Puppeteer examples"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/examples"},"children":["Automation Examples"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Complete Linux stress test script"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://github.com/MoreLoginBrowser/MoreLogin-API-Demos/blob/main/MoreLogin-Python/linux_server_test.py"},"children":["GitHub — linux_server_test.py"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["CLI quick start"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/cli/quick-start"},"children":["CLI Guide"]}]}]}]}]}]}]},"headings":[{"value":"Linux Server Deployment","id":"linux-server-deployment","depth":1},{"value":"What You'll Achieve","id":"what-youll-achieve","depth":2},{"value":"Architecture Overview","id":"architecture-overview","depth":2},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"Step 1 — Install System Dependencies","id":"step-1--install-system-dependencies","depth":2},{"value":"Install Fonts (Optional)","id":"install-fonts-optional","depth":3},{"value":"Step 2 — Download & Launch MoreLogin","id":"step-2--download--launch-morelogin","depth":2},{"value":"2.1 Download the AppImage","id":"21-download-the-appimage","depth":3},{"value":"2.2 Start MoreLogin in the Background","id":"22-start-morelogin-in-the-background","depth":3},{"value":"Step 3 — Log In to MoreLogin via API (Required)","id":"step-3--log-in-to-morelogin-via-api-required","depth":2},{"value":"3.1 Get Your API Credentials","id":"31-get-your-api-credentials","depth":3},{"value":"3.2 Log In via curl","id":"32-log-in-via-curl","depth":3},{"value":"3.3 Verify the Login","id":"33-verify-the-login","depth":3},{"value":"Step 4 — Configure Network Forwarding (socat)","id":"step-4--configure-network-forwarding-socat","depth":2},{"value":"4.1 Forward the Local API Port","id":"41-forward-the-local-api-port","depth":3},{"value":"4.2 Forward CDP Debug Ports","id":"42-forward-cdp-debug-ports","depth":3},{"value":"4.3 Open Firewall Ports","id":"43-open-firewall-ports","depth":3},{"value":"4.4 Recommended: Use SSH Tunnel Instead","id":"44-recommended-use-ssh-tunnel-instead","depth":3},{"value":"Step 5 — Quick Automation Example","id":"step-5--quick-automation-example","depth":2},{"value":"5.1 Install Python Dependencies","id":"51-install-python-dependencies","depth":3},{"value":"5.2 Create → Start → Automate → Cleanup","id":"52-create--start--automate--cleanup","depth":3},{"value":"Performance Benchmark","id":"performance-benchmark","depth":2},{"value":"Production Deployment (systemd)","id":"production-deployment-systemd","depth":2},{"value":"Create the Service File","id":"create-the-service-file","depth":3},{"value":"Enable and Start","id":"enable-and-start","depth":3},{"value":"Manage the Service","id":"manage-the-service","depth":3},{"value":"Troubleshooting","id":"troubleshooting","depth":2},{"value":"MoreLogin fails to start","id":"morelogin-fails-to-start","depth":3},{"value":"Browser pages show blank text / missing characters","id":"browser-pages-show-blank-text--missing-characters","depth":3},{"value":"CDP connection refused from external machine","id":"cdp-connection-refused-from-external-machine","depth":3},{"value":"curl to Local API returns \"Connection refused\"","id":"curl-to-local-api-returns-connection-refused","depth":3},{"value":"API returns 401 \"login status has expired\"","id":"api-returns-401-login-status-has-expired","depth":3},{"value":"Next Steps","id":"next-steps","depth":2}],"frontmatter":{"seo":{"title":"Linux Server Deployment"}},"lastModified":"2026-05-21T13:31:34.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/linux-server/overview","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}