{"id":16,"date":"2023-01-09T17:11:13","date_gmt":"2023-01-09T09:11:13","guid":{"rendered":"https:\/\/deigel.me\/blog\/?p=16"},"modified":"2026-01-31T20:59:52","modified_gmt":"2026-01-31T12:59:52","slug":"code-server-on-vps","status":"publish","type":"post","link":"https:\/\/deigel.me\/blog\/?p=16","title":{"rendered":"Code-server on VPS"},"content":{"rendered":"<hr \/>\n<h2>title: &#8220;code-server on a tiny VPS: my iPad-friendly dev setup&#8221;\ndate: 2023-01-09 20:20:00 -0800\nslug: code-server-remote-dev\ncategories: [Dev Environment]\ntags: [VS Code, code-server, Nginx, iPad, Reverse Proxy]\nsummary: Spin up code-server, put it behind HTTPS with a password, and use it comfortably from an iPad (or any browser).<\/h2>\n<h1>code-server on a tiny VPS: my iPad-friendly dev setup<\/h1>\n<blockquote>\n<p>TL;DR: keep code-server bound to localhost, reverse-proxy it over HTTPS, use a strong password (or Basic Auth in front), and you\u2019re done.<\/p>\n<\/blockquote>\n<h2>1) Install code-server<\/h2>\n<pre><code class=\"lang-bash language-bash bash\">curl -fsSL https:\/\/code-server.dev\/install.sh | sh\n\n# start and enable (system service)\nsudo systemctl enable --now code-server@$USER\nsudo systemctl status code-server@$USER --no-pager<\/code><\/pre>\n<h2>2) Configure it to listen only on localhost<\/h2>\n<pre><code class=\"lang-bash language-bash bash\">mkdir -p ~\/.config\/code-server\ncat &gt; ~\/.config\/code-server\/config.yaml &lt;&lt;&#039;YAML&#039;\nbind-addr: 127.0.0.1:8080\nauth: password\npassword: &quot;please-change-me-quickly&quot;\ncert: false\nYAML\n\n# reload after changing config\nsudo systemctl restart code-server@$USER<\/code><\/pre>\n<blockquote>\n<p>Keep port <strong>not<\/strong> exposed publicly. We\u2019ll publish it via Nginx over HTTPS.<\/p>\n<\/blockquote>\n<h2>3) Reverse proxy with Nginx (subdomain)<\/h2>\n<p>I prefer a subdomain like <code>code.example.com<\/code>\u2014simpler than subpaths.<\/p>\n<pre><code class=\"lang-nginx language-nginx nginx\"># \/etc\/nginx\/conf.d\/code.conf\nserver {\n    listen 443 ssl http2;\n    server_name code.example.com;\n\n    # TLS certs here...\n    ssl_certificate     \/etc\/ssl\/your.crt;\n    ssl_certificate_key \/etc\/ssl\/your.key;\n\n    # (optional but recommended) Basic Auth in front of code-server\n    auth_basic           &quot;Restricted&quot;;\n    auth_basic_user_file \/etc\/nginx\/.htpasswd;\n\n    location \/ {\n        proxy_pass http:\/\/127.0.0.1:8080\/;\n        proxy_http_version 1.1;\n        proxy_set_header Host              $host;\n        proxy_set_header Upgrade           $http_upgrade;\n        proxy_set_header Connection        $connection_upgrade;\n        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 3600;\n    }\n}<\/code><\/pre>\n<p>Create Basic Auth credentials:<\/p>\n<pre><code class=\"lang-bash language-bash bash\">sudo apt install -y apache2-utils\nsudo htpasswd -c \/etc\/nginx\/.htpasswd coder\nsudo nginx -t &amp;&amp; sudo systemctl reload nginx<\/code><\/pre>\n<blockquote>\n<p>If you <strong>must<\/strong> use a subpath like <code>\/code\/<\/code>, set <code>proxy_pass http:\/\/127.0.0.1:8080\/;<\/code> and run code-server with <code>--base-path \/code<\/code> (or equivalent config). Subdomains are less finicky.<\/p>\n<\/blockquote>\n<h2>4) iPad usage<\/h2>\n<ul>\n<li>Open <code>https:\/\/code.example.com<\/code> in Safari, sign in, then \u201cAdd to Home Screen\u201d for a nice full-screen PWA.<\/li>\n<li>A Bluetooth keyboard makes it feel like desktop VS Code.<br \/>\nCommon shortcuts: <code>Cmd+P<\/code> (Quick Open), <code>Cmd+Shift+P<\/code> (Command Palette), <code>Ctrl+`<\/code> (terminal).<\/li>\n<\/ul>\n<h2>5) Security notes<\/h2>\n<ul>\n<li><strong>Do not<\/strong> expose <code>:8080<\/code> to the Internet.<\/li>\n<li>Use long, unique passwords (or keep Basic Auth in front).<\/li>\n<li>Consider Cloudflare Access or your SSO if you already use it.<\/li>\n<li>Keep code-server updated: <code>sudo systemctl restart code-server@$USER<\/code> after upgrades.<\/li>\n<\/ul>\n<h2>6) QoL tips<\/h2>\n<ul>\n<li>Set your Git identity and SSH keys once in the server shell.<\/li>\n<li>Install extensions you actually need; the rest can live on your laptop.<\/li>\n<li>For big files or terminals that run for hours, bump <code>proxy_read_timeout<\/code>.<\/li>\n<\/ul>\n<p>That\u2019s the whole setup. It\u2019s been comfy from my iPad and boringly reliable.<\/p>","protected":false},"excerpt":{"rendered":"<p>title: &#8220;code-server on a tiny VPS: my iPad-friend [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_import_markdown_pro_load_document_selector":0,"_import_markdown_pro_submit_text_textarea":"","footnotes":""},"categories":[2],"tags":[],"class_list":["post-16","post","type-post","status-publish","format-standard","hentry","category-vps"],"_links":{"self":[{"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/16","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=16"}],"version-history":[{"count":3,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/16\/revisions"}],"predecessor-version":[{"id":31,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=\/wp\/v2\/posts\/16\/revisions\/31"}],"wp:attachment":[{"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/deigel.me\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}