<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged ubuntu</title>
<link>https://coderlegion.com/tag/ubuntu</link>
<description>Powered by Question2Answer</description>
<item>
<title>Node 22.22 runs TypeScript. Ubuntu's Node 22.22 doesn't.</title>
<link>https://coderlegion.com/32697/node-22-22-runs-typescript-ubuntus-node-22-22-doesnt</link>
<description>&lt;p&gt;Four test files in my site's repo import TypeScript directly. &lt;code&gt;faq.test.mjs&lt;/code&gt; pulls in &lt;code&gt;src/lib/faq.ts&lt;/code&gt;, and three others do the same with their own modules. There is no build step and no loader in between: the script is &lt;code&gt;node --test &quot;test/*.test.mjs&quot;&lt;/code&gt;, and type stripping has been on by default since Node 22.18.0, so Node strips the types and runs what's left.&lt;/p&gt;
&lt;p&gt;In CI those tests pass. The workflow asks &lt;code&gt;actions/setup-node&lt;/code&gt; for &lt;code&gt;node-version: &quot;22&quot;&lt;/code&gt;, and the latest runs on &lt;code&gt;main&lt;/code&gt; are green.&lt;/p&gt;
&lt;p&gt;On my Ubuntu server, with Node 22.22.1, all four fail before a single assertion runs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension &quot;.ts&quot; for .../src/lib/faq.ts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;22.22.1 is newer than 22.18.0, so on paper the feature is there. &lt;strong&gt;The version number was right. The binary behind it was a different build.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;same-version-different-binary&quot;&gt;Same version, different binary&lt;/h3&gt;
&lt;p&gt;Node will tell you whether it strips types. The v22 docs describe &lt;code&gt;process.features.typescript&lt;/code&gt; as &lt;code&gt;&quot;strip&quot;&lt;/code&gt; by default, &lt;code&gt;&quot;transform&quot;&lt;/code&gt; with &lt;code&gt;--experimental-transform-types&lt;/code&gt;, and &lt;code&gt;false&lt;/code&gt; if Node is started with &lt;code&gt;--no-experimental-strip-types&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I did not pass that flag:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-console&quot;&gt;$ /usr/bin/node --version
v22.22.1
$ /usr/bin/node -p 'process.features.typescript'
false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That &lt;code&gt;node&lt;/code&gt; comes from Ubuntu's &lt;code&gt;nodejs&lt;/code&gt; package, version &lt;code&gt;22.22.1+dfsg+~cs22.19.15-1ubuntu1&lt;/code&gt;. Asking for the feature explicitly does not bring it back:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-console&quot;&gt;$ /usr/bin/node --experimental-strip-types t.ts
node:internal/util:226
    throw new ERR_NO_TYPESCRIPT();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To rule out the version itself, I downloaded the official 22.22.1 build from nodejs.org, checked it against &lt;code&gt;SHASUMS256.txt&lt;/code&gt;, and asked the same question. It printed &lt;code&gt;strip&lt;/code&gt; and ran the same &lt;code&gt;.ts&lt;/code&gt; file without complaint.&lt;/p&gt;
&lt;h3 id=&quot;where-typescript-went&quot;&gt;Where TypeScript went&lt;/h3&gt;
&lt;p&gt;The package's own changelog explains most of it. The December 2024 entry, &lt;code&gt;22.12.0+dfsg-1&lt;/code&gt;, says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;dfsg-exclude amaro, build without it - it requires swc. This disables the ability to execute TypeScript files using the --experimental-strip-types flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The November 2025 entry, &lt;code&gt;22.21.1+dfsg+~cs22.19.0-1&lt;/code&gt;, says: &quot;we build without-amaro for now, disable strip-types&quot;.&lt;/p&gt;
&lt;p&gt;The March 2026 Debian entry, merged into the version I have, says: &quot;Drop 'no amaro' patch, solved upstream&quot;. The build still answers &lt;code&gt;false&lt;/code&gt;, and it reports how it was compiled:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-console&quot;&gt;$ /usr/bin/node -p 'process.config.variables.node_use_amaro'
false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The official 22.22.1 build prints &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The changelog doesn't say what &quot;solved upstream&quot; changed. My tests only see what the build does.&lt;/p&gt;
&lt;h3 id=&quot;why-ci-never-saw-it&quot;&gt;Why CI never saw it&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;setup-node&lt;/code&gt; README says the action first checks the runner's tool cache, then pulls LTS versions from the &lt;code&gt;actions/node-versions&lt;/code&gt; releases and, on a miss, falls back to downloading from nodejs.org. None of those is Ubuntu's package. The four test files pass in CI, so the Node there strips types. The one on my server does not, and both report major version 22.&lt;/p&gt;
&lt;p&gt;That is how one repository can be green in CI and red on my server without a line of code changing.&lt;/p&gt;
&lt;h3 id=&quot;the-fix&quot;&gt;The fix&lt;/h3&gt;
&lt;p&gt;I extracted the official Node 22.23.2 tarball into &lt;code&gt;/usr/local&lt;/code&gt;. &lt;code&gt;/usr/local/bin&lt;/code&gt; comes before &lt;code&gt;/usr/bin&lt;/code&gt; in &lt;code&gt;PATH&lt;/code&gt;, so &lt;code&gt;node&lt;/code&gt; now resolves to the official build, with npm 10.9.8 bundled.&lt;/p&gt;
&lt;p&gt;I left the Ubuntu package installed. On this machine 15 installed packages depend on &lt;code&gt;nodejs&lt;/code&gt;: &lt;code&gt;handlebars&lt;/code&gt; and 14 &lt;code&gt;node-*&lt;/code&gt; libraries. Removing it to get the right &lt;code&gt;node&lt;/code&gt; would have meant fighting apt over things that have nothing to do with my site.&lt;/p&gt;
&lt;p&gt;Same four files, official binary:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tap&quot;&gt;# tests 42
# pass 42
# fail 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;ask-node-what-it-can-do&quot;&gt;Ask Node what it can do&lt;/h3&gt;
&lt;p&gt;If a project relies on type stripping, &lt;code&gt;node --version&lt;/code&gt; is not the check. This is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;node -p 'process.features.typescript'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the two builds above it prints &lt;code&gt;strip&lt;/code&gt; when the feature works and &lt;code&gt;false&lt;/code&gt; when it doesn't, whatever the version string says. It belongs in a setup script next to the version check, because on my server the version check was the one that passed.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32697/node-22-22-runs-typescript-ubuntus-node-22-22-doesnt</guid>
<pubDate>Tue, 27 Oct 2026 14:27:58 +0000</pubDate>
</item>
<item>
<title>From Silence to Sound: Fixing Audio on Ubuntu Linux on my Samsung Galaxy Book</title>
<link>https://coderlegion.com/32587/from-silence-to-sound-fixing-audio-on-ubuntu-linux-on-my-samsung-galaxy-book</link>
<description>&lt;p&gt;Over four days. That’s how long I spent trying to get the internal speakers on my Samsung Galaxy Book2 (NT950XDA) to produce a single sound after switching from Windows to Ubuntu.&lt;/p&gt;
&lt;p&gt;And I wasn’t alone.&lt;/p&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Right out of the box, Ubuntu (even the latest 24.04) doesn’t play well with the Realtek ALC298 audio codec on many Samsung Galaxy Book devices. Headphones might work, but internal speakers remain silent due to uninitialized smart speaker amps.&lt;/p&gt;
&lt;p&gt;I found myself crawling through obscure forum posts, GitHub issues, and deep dmesg logs. I came across broken workarounds, kernel parameters, even dead-end firmware downloads. Nothing stuck — until I discovered a promising thread from the Manjaro community and a early work of @&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/joshuagrisham/samsung-galaxybook-extras&quot;&gt;joshuagrisham&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-breakthrough&quot;&gt;The Breakthrough&lt;/h3&gt;
&lt;p&gt;The answer turned out to be &lt;code&gt;hda-verb&lt;/code&gt;, a tool for sending manual codec commands to initialize the amp. After copying dozens of lines of hda-verb commands into a script and running it manually, audio finally burst through my speakers.&lt;/p&gt;
&lt;p&gt;It worked—but only until the next reboot.&lt;/p&gt;
&lt;h3 id=&quot;the-automation&quot;&gt;The Automation&lt;/h3&gt;
&lt;p&gt;That’s when I decided to build a real solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto-run the fix at boot and after resume&lt;/li&gt;
&lt;li&gt;Avoid breaking any existing audio setups&lt;/li&gt;
&lt;li&gt;Use systemd services for reliability&lt;/li&gt;
&lt;li&gt;Keep it simple, safe, and open source&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-tool-galaxybook-linux-setup&quot;&gt;The Tool: galaxybook-linux-setup&lt;/h3&gt;
&lt;p&gt;Now available on GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/topzyray/galaxybook-linux-setup&quot;&gt;https://github.com/topzyray/galaxybook-linux-setup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initializes smart speaker amps via hda-verb&lt;/li&gt;
&lt;li&gt;Runs fix on boot + resume using systemd&lt;/li&gt;
&lt;li&gt;Disables PulseAudio's suspend-on-idle to prevent speaker freezing&lt;/li&gt;
&lt;li&gt;Backs up existing config files to avoid damage&lt;/li&gt;
&lt;li&gt;⚙️ Detects your current user and system safely&lt;/li&gt;
&lt;li&gt;MIT-licensed, contributions welcome&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Installation is as easy as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/topzyray/galaxybook-linux-setup.git
cd galaxybook-linux-setup
chmod +x install.sh
./install.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reboot when prompted, and you’re done. No more silent boots.&lt;/p&gt;
&lt;h3 id=&quot;who-this-might-helps&quot;&gt;Who This Might Helps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Galaxy Book2 NT950XDA users or other device with similar &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/joshuagrisham/samsung-galaxybook-extras/issues/80&quot;&gt;issue or device/driver info&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Anyone with Realtek ALC298 codec and no speaker output&lt;/li&gt;
&lt;li&gt;Linux newcomers switching from Windows&lt;/li&gt;
&lt;li&gt;Ubuntu/Mint/Pop_OS/Manjaro users on 5.8+ kernels though I tested on 6.14.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;credit-where-due&quot;&gt;Credit Where Due&lt;/h3&gt;
&lt;p&gt;Joshua Grisham @joshuagrisham – &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/joshuagrisham/samsung-galaxybook-extras&quot;&gt;GitHub&lt;/a&gt;&lt;br&gt;
Community post from user @drlucas – &lt;a rel=&quot;nofollow&quot; href=&quot;https://forum.manjaro.org/t/howto-set-up-the-audio-card-in-samsung-galaxy-book/37090&quot;&gt;Manjaro Forum&lt;/a&gt;&lt;br&gt;
Access hda-ver used &lt;a rel=&quot;nofollow&quot; href=&quot;https://pastebin.com/raw/zsXp2vz6&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-bigger-picture&quot;&gt;The Bigger Picture&lt;/h3&gt;
&lt;p&gt;This project reminded me why open source matters. Without the knowledge of those before me, I’d still be rebooting into silence.&lt;/p&gt;
&lt;p&gt;Now, I’m extending this to include fingerprint readers, screen brightness fixes, and more for Galaxy Book users and I will also be testing on other devices. Please you can test too. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE: The script does not delete any file rather it create a backup whenever updating an existing file.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Have a similar issue? Found this useful? Contribute, fork, share, or open an issue!&lt;/p&gt;
&lt;p&gt; Repo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/topzyray/galaxybook-linux-setup&quot;&gt;https://github.com/topzyray/galaxybook-linux-setup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you have further question, suggestion or observations please leave them in the comment section. Thank you for reading.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32587/from-silence-to-sound-fixing-audio-on-ubuntu-linux-on-my-samsung-galaxy-book</guid>
<pubDate>Mon, 26 Oct 2026 19:05:37 +0000</pubDate>
</item>
<item>
<title>Ubuntu Resolute: Evolution Instead of Revolution</title>
<link>https://coderlegion.com/27112/ubuntu-resolute-evolution-instead-of-revolution</link>
<description>&lt;p&gt;With the 2026 Ubuntu LTS release, we’re seeing evolution instead of revolution across the board. Mostly small version updates, a recent kernel in version 7, and many updates under the hood.&lt;/p&gt;
&lt;p&gt;An overview of available software versions for the most commonly used services can be found in our documentation.&lt;/p&gt;
&lt;h2 id=&quot;what-does-it-take-to-implement-a-new-ubuntu-version-&quot;&gt;What Does It Take to Implement a New Ubuntu Version?&lt;/h2&gt;
&lt;p&gt;Have you ever asked yourself what it takes us to implement a new Ubuntu LTS version in our ecosystem? It’s a little more complicated than imaging a USB stick. We’ll take you through our implementation journey.&lt;/p&gt;
&lt;h3 id=&quot;an-early-look&quot;&gt;An Early Look&lt;/h3&gt;
&lt;p&gt;On April 23, 2026, Ubuntu Resolute was publicly released, with a “Beta” preview a month prior.&lt;/p&gt;
&lt;p&gt;Shortly after the Beta release, we had a first look at the new release. With clarity about the used Ruby version, we knew what it would take to update our self-service tools such as nine-manage-vhosts, nine-manage-databases, and FTPAdmin2. We also run some tools not directly used by customers but of importance to us and our automation landscape.&lt;/p&gt;
&lt;p&gt;Following our first hands-on, we created the backlog items we considered mandatory for the implementation.&lt;/p&gt;
&lt;h3 id=&quot;step-by-step&quot;&gt;Step by Step&lt;/h3&gt;
&lt;p&gt;The first issue to solve was around the package manager and package sources. With some deprecations in apt 3.1, updates for our automation around our package mirrors (we mirror the Canonical repositories for all Ubuntu versions) as well as package sources became necessary.&lt;/p&gt;
&lt;p&gt;After addressing these first issues, we needed to build a “packagebuilder” image for the new Ubuntu version. This container image is used in automated pipelines to build Debian packages like nine-manage-vhosts and nine-manage-databases, but also Prometheus exporters.&lt;/p&gt;
&lt;p&gt;Prometheus is used for monitoring at Nine, and exporters are the metrics source our alerting is based on. We build more than 20 exporters that aren’t uniformly designed and need customisations in their build processes.&lt;/p&gt;
&lt;p&gt;To test these packages in a “real” system environment (as opposed to a container, where things like systemd are missing), we built a VirtualBox VM image of Ubuntu Resolute, which we use to run tests of our self-built packages.&lt;/p&gt;
&lt;p&gt;Once we had finished building our automation and monitoring tools, we could dive deeper into the automation of our Managed Services.&lt;/p&gt;
&lt;p&gt;With Ubuntu Resolute, many services got updated systemd units and AppArmor profiles. This improves security but also required some changes and tests, for example to our Apache and MariaDB automation.&lt;/p&gt;
&lt;p&gt;One of the changes to systemd resulted in /tmp partitions or folders that are no longer backed by system storage. Instead, /tmp uses a tmpfs partition type, which uses system memory to store data.&lt;/p&gt;
&lt;p&gt;System memory is valuable, and many applications and services use /tmp for short-lived data that might require quite some storage. A tmpfs partition doesn’t meet our requirements for a server environment, so we reverted to a storage-based solution.&lt;/p&gt;
&lt;p&gt;Most of these tasks are independent of the type of system, virtual or dedicated. But of course, we also needed to adapt our automated dedicated server provisioning for Ubuntu Resolute.&lt;/p&gt;
&lt;h3 id=&quot;a-little-revolution&quot;&gt;A Little Revolution&lt;/h3&gt;
&lt;p&gt;In the past years, the companies behind Elasticsearch and Redis have tried to further commercialise their open-source services and changed the licensing model of their software.&lt;/p&gt;
&lt;p&gt;Although both companies have since returned to open-source licenses, rebuilding lost trust is difficult. In the meantime, the open-source ecosystem has continued to evolve.&lt;/p&gt;
&lt;p&gt;In our offering, Elasticsearch has since been superseded by OpenSearch.&lt;/p&gt;
&lt;p&gt;After Redis announced a license change in 2024, we thought we were prepared, as we had already implemented KeyDB as an alternative a year before. What looked promising at first (e.g. Redis compatibility, the possibility to run multi-primary replication) has turned out to be a dead end: the KeyDB project has since been abandoned.&lt;/p&gt;
&lt;p&gt;In 2024, following the license change, another Redis fork named Valkey was released. Broad support from industry leaders such as AWS, Google Cloud, Percona, Oracle, and more has helped Valkey gain momentum quickly and establish itself as the de facto alternative to Redis.&lt;/p&gt;
&lt;p&gt;With Ubuntu Resolute, Valkey 9 has found its way into the Ubuntu “main” repository. The main repository contains software that’s supported by Canonical during the entire lifecycle of the Ubuntu release.&lt;/p&gt;
&lt;p&gt;As Valkey in version 9 is fully compatible with Redis 7.2.4, and Redis remains in the “universe” repository (a “community maintained” repository), the decision to use Valkey from Ubuntu Resolute onward was an easy one for us: fully compatible with the latest Redis version we offered (7.0 in Ubuntu Noble), but actively supported throughout all lifecycle phases of Ubuntu.&lt;/p&gt;
&lt;h2 id=&quot;engineering-summary&quot;&gt;Engineering Summary&lt;/h2&gt;
&lt;p&gt;This implementation journey doesn’t just provide a broad overview of some technical challenges, it also gives an idea of how Nine engineers work and which tools and processes we use.&lt;/p&gt;
&lt;p&gt;Many of the backlog items iteratively built on each other. Besides the engineering and quality control effort, we also need to ensure that changes don’t cause side effects on older Ubuntu versions, since all versions are automated through the same codebase.&lt;/p&gt;
&lt;p&gt;After 76 merge requests linked to 20 backlog items, the first Ubuntu Resolute systems were delivered to our customers on June 29, 2026.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27112/ubuntu-resolute-evolution-instead-of-revolution</guid>
<pubDate>Mon, 14 Sep 2026 04:35:11 +0000</pubDate>
</item>
<item>
<title>Ansible Installation and Configuration On Ubuntu</title>
<link>https://coderlegion.com/22079/ansible-installation-and-configuration-on-ubuntu</link>
<description>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;h4 id=&quot;what-is-ansible-&quot;&gt;What is Ansible?&lt;/h4&gt;
&lt;p&gt;Ansible is an automation language that can describe any IT environment, whether home-lab or large-scale infrastructure. It is easy to learn, beautiful code that reads like clear documentation.&lt;/p&gt;
&lt;p&gt;There are many repetitive tasks that we may perform on a server, a VM, or a VPS. The tasks can range from setting up SSH keys to disabling the root user to firewall configs. If you manage multiple servers daily and you have to do the same configuration over and over, Ansible can automate the process, allowing you to increase your productivity.&lt;/p&gt;
&lt;p&gt;It requires installing Ansible on the Control Node and just having Python 3 installed on the Managed Node.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-control-node-&quot;&gt;What is the Control Node?&lt;/h4&gt;
&lt;p&gt;It's simply the system that Ansible is installed on that controls the remote machines.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-managed-node-&quot;&gt;What is the Managed Node?&lt;/h4&gt;
&lt;p&gt;A remote system, or host, that Ansible controls. Ansible is agent-less, so you don’t need to install Ansible on the managed node; you simply need Python 3 to be installed.&lt;/p&gt;
&lt;h3 id=&quot;install-ansible-on-ubuntu-control-node-local-machine-running-zorinos-&quot;&gt;Install Ansible on Ubuntu (Control Node—local machine. Running ZorinOS).&lt;/h3&gt;
&lt;p&gt;your-command-prompt:~$ sudo apt update&lt;/p&gt;
&lt;p&gt;your-command-prompt:~$ sudo apt install software-properties-common&lt;/p&gt;
&lt;p&gt;your-command-prompt:~$ sudo add-apt-repository --yes --update ppa:ansible/ansible&lt;/p&gt;
&lt;p&gt;your-command-prompt:~$ sudo apt install ansible&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  Ensure that Python 3 is installed on your remote server. At this time of writing, Ubuntu 26.04 LTS has Python 3.14.4 installed by default. This is enough to get your remote server running with Ansible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;create-an-inventory-folder-with-a-host-file-&quot;&gt;Create an inventory folder with a host file.&lt;/h3&gt;
&lt;p&gt;This file contains a list of the remote machines, VMs, or VPSs that you want to control using Ansible.&lt;/p&gt;
&lt;p&gt;For this step you can use a code editor; I will be using Vs Code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a folder called Ansible and open it in vs code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In that folder, create another folder and name it inventory&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the inventory folder, create a file named hosts with no extension&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8019618342096067034&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;hosts file&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Start with an attribute; give it whatever name you want.&lt;br&gt;
[servers]&lt;/p&gt;
&lt;p&gt;Then list the servers that you want to manage using their DNS names or their IP addresses.&lt;br&gt;
[servers]&lt;br&gt;
vpsServer ansible_host=10.10.100.45&lt;br&gt;
work-ToRule&lt;br&gt;
10.10.45.62&lt;/p&gt;
&lt;p&gt;We can give our hosts an alias by passing in their IP address to any name we wish to give it. In the list above, I have given the VPS with IP address 10.10.100.45 an alias of vpsServer. (IP address, just an example)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=1561479094875298780&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;create-a-folder-named-playbook&quot;&gt;Create a folder named Playbook&lt;/h3&gt;
&lt;p&gt;In this folder we will create the actual yml files that we will use to automate the different processes we want to run on our remote servers. It can be an update, firewall config or install a package.&lt;/p&gt;
&lt;p&gt;Our first playbook will update the Ubuntu server and set the right timezone. I will name it apt-update.yml.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10832088875540847717&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;examine-the-yml-file-&quot;&gt;Examine the YML file.&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;- hosts: &quot;*&quot;&lt;/strong&gt; — this means all remote hosts will be updated. A single host can be selected by either its alias, DNA name, or IP address.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;become: &lt;/strong&gt;  true—gives Ansible the administrative authority to act as a sudo user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;serial: 1 &lt;/strong&gt; —In Ansible, serial: 1 tells the automation engine to manage your servers one by one rather than updating all of them at the same time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tasks &lt;/strong&gt;—In Ansible, the tasks section is structured as a list of individual actions. Each task is a YAML block that breaks down into four main parts: the Identity, the Module, the Arguments, and Directives.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;- name: &lt;/strong&gt; This is a plain-text description of what the task does and always starts with a dash or hyphen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; The Collection (community.general): &lt;/strong&gt;An Ansible Collection is a standardized packaging format used to bundle and distribute Ansible content, including playbooks, roles, modules, and plugins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; The Module (timezone): &lt;/strong&gt; This is the specialized tool or command line plugin you want to use. It tells Ansible how to execute the action on the server. Instead of writing raw terminal commands, you use abstract modules like apt (for packages), copy (for files), user (for accounts), or service (for daemons).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Parameters (name: America/Port_of_Spain): &lt;/strong&gt; These are the specific settings, options, or instructions you pass into the module. It defines the exact parameters of your action. The name: parameter is a mandatory argument required by the community.general.timezone module. It specifies the exact geographical timezone identifier you want to apply to the target Linux operating system.&lt;/p&gt;
&lt;p&gt;If you're new to Ansible, you can find a collection of the modules and arguments available @ &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.ansible.com/projects/ansible/latest/collections/index.html&quot;&gt;https://docs.ansible.com/projects/ansible/latest/collections/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=4683817883742389333&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;run-playbook&quot;&gt;Run Playbook&lt;/h3&gt;
&lt;p&gt;Connect to the remote host in your terminal.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;your-command-prompt:~$ ANSIBLE_HOST_KEY_CHECKING=FALSE ansible -i ./inventory/hosts vpsServer -m ping --user root --ask-pass&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ANSIBLE_HOST_KEY_CHECKING=FALSE&lt;/strong&gt;&lt;br&gt;
This is a temporary environment variable that changes how Ansible handles security signatures. It tells Ansible to skip looking inside your local ~/.ssh/known_hosts file. It allows you to connect to a fresh or reinstalled VPS instantly without throwing the strict “Host key verification failed” fingerprint error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ansible&lt;/strong&gt;&lt;br&gt;
This invokes the ad-hoc command line engine (used for running single, quick tasks instead of running an entire playbook file)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-i ./inventory/hosts&lt;/strong&gt;&lt;br&gt;
The -i flag stands for Inventory. It tells Ansible exactly where your infrastructure map file is saved. In this case, you are instructing it to bypass any default system paths and read the specific file named hosts located inside your ./inventory/ folder&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;vpsServer&lt;/strong&gt;&lt;br&gt;
This is the specific destination target. Ansible looks into your inventory to find a server or group named exactly vpsServer to run the task against.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-m ping&lt;/strong&gt;&lt;br&gt;
The module flag. It tells Ansible to execute the built-in ping software module. This is a diagnostic module that logs into the server, checks if Python is available, and returns a successful green&quot;pong&quot;if the link is healthy.&lt;/p&gt;
&lt;p&gt;Once you get a green ‘pong’ you can run the next command.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;your-command-prompt:~$ ansible-playbook ./playbooks/apt-update.yml --user root -e “ansible_port=22&quot; --ask-pass --ask-become-pass -i ./inventory/hosts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ansible-playbook&lt;/strong&gt;&lt;br&gt;
This invokes Ansible's playbook execution engine. Unlike the simple ansible command (which runs just one quick ad-hoc task), ansible-playbook is designed to open an automation script file and run a whole sequence of structured tasks in order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;./playbooks/apt-update.yml&lt;/strong&gt;&lt;br&gt;
This is the relative file path to the exact automation script you want to run. : You are telling Ansible to open the file named apt-update.yml inside your playbooks folder and execute whatever configuration steps (like package updates or installations) are written inside it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;--user root&lt;/strong&gt;&lt;br&gt;
This asks Ansible to establish the initial SSH network connection using the root account, completely ignoring any other default usernames written inside your configuration files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;-e “ansible_port=22”&lt;/strong&gt;&lt;br&gt;
The -e flag stands for Extra Variables. This dynamically injects a temporary variable into the execution. Here, you are explicitly forcing Ansible to connect over standard Port 22, overriding any custom ports (like 2222) saved in your host files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;--ask-pass&lt;/strong&gt;&lt;br&gt;
Tells Ansible that you are using password authentication instead of an SSH key token. It forces the terminal to prompt you for the root SSH login password when you launch the command.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;--ask-become-pass&lt;/strong&gt;&lt;br&gt;
This tells Ansible to prompt you for the privilege escalation (sudo) password.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Note: &lt;/strong&gt; Since you are logging in directly as the absolute system master (root),--ask-become-pass is actually redundant here because root already has absolute power and never needs to escalate further.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;-i ./inventory/hosts&lt;/strong&gt;&lt;br&gt;
The -i flag maps your Inventory. It tells the playbook engine exactly where to look to find your list of servers. You are directing it to read the specific hosts file located inside your ./inventory/ folder.&lt;/p&gt;
&lt;h3 id=&quot;summary-of-the-execution-flow&quot;&gt;Summary of the Execution Flow&lt;/h3&gt;
&lt;p&gt;When you execute this command from your Control Node terminal, Ansible handles it in this sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It opens your./inventory/hostsfile to look up the IP address of the target server.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It stops and asks you to type the SSH password (due to –ask-pass).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It stops and asks you to type the Sudo password (due to –ask-become-pass).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It bypasses port variables, connects directly to Port 22 as root, opens up your apt-update.yml playbook, and begins executing its tasks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I want to give a shout-out to Aldo @&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/aldo_cve&quot;&gt;https://dev.to/aldo_cve&lt;/a&gt;. He recommended Ansible in a previous post I published, and I looked it up.&lt;/p&gt;
&lt;p&gt;I found Ansible to be a great addition to my workflow in setting up my backend. I hope you guys enjoyed this walk through. You can look forward to more posts where I publish some of the playbooks that I find useful.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22079/ansible-installation-and-configuration-on-ubuntu</guid>
<pubDate>Wed, 08 Jul 2026 00:14:52 +0000</pubDate>
</item>
<item>
<title>Surviving a Kernel Panic: My Ubuntu War Story</title>
<link>https://coderlegion.com/15509/surviving-a-kernel-panic-my-ubuntu-war-story</link>
<description>&lt;p&gt;When your backlog is full of data science models and software engineering tasks, the last thing you need is your OS failing to boot because of a kernel panic, no?&lt;/p&gt;
&lt;p&gt;Well, this happened to me. I was given the option to reboot, did it the first time, and I was back in after choosing an older kernel version from the menu. It happened a second time, a third time... and I was just okay with it, saying, &quot;I'll fix it later.&quot; &lt;br&gt;
This worked like a charm right up until Linux decided it had had enough. It finally threw an &lt;code&gt;Input/output error&lt;/code&gt;, locked the root filesystem &lt;code&gt;/&lt;/code&gt; into &lt;code&gt;Read-only&lt;/code&gt; mode, and locked me out completely.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sr79ity097cgbe96614.jpeg&quot; alt=&quot;A laptop screen showing a Linux terminal failing to execute commands, flooded with &amp;quot;Read-only file system&amp;quot; and &amp;quot;Input/output error&amp;quot; messages.&quot;&gt;&lt;br&gt;
I couldn't even poweroff via the terminal.&lt;/p&gt;
&lt;p&gt;The silver lining came when I realized I had a Kali Linux installer ISO sitting on a flash drive nearby. In case you find yourself in this,let's call it &quot;very specific&quot; situation, here is how to perform open-heart surgery on your system before you do anything rash like wiping your drive.&lt;/p&gt;
&lt;h3 id=&quot;step-1-the-hard-reset-amp-the-usb-boot&quot;&gt;Step 1: The Hard Reset &amp;amp; The USB Boot&lt;/h3&gt;
&lt;p&gt;Since the terminal was completely frozen, a graceful shutdown was out of the question. Long-press the power button to force the PC to power off. Turn it back on and immediately open your startup menu (usually by spamming Esc, F12, or F9 depending on your laptop model). From there, boot directly into your rescue USB.&lt;/p&gt;
&lt;h3 id=&quot;step-2-dropping-into-the-underworld-the-busybox-shell-&quot;&gt;Step 2: Dropping into the Underworld (The BusyBox Shell)&lt;/h3&gt;
&lt;p&gt;Because I was using a Kali Linux installer ISO (not a Live Desktop), there was no beautiful graphical interface to save me. I had to navigate the installer menu and select &quot;Execute a shell&quot;.&lt;/p&gt;
&lt;p&gt;This drops you into a raw, stripped-down BusyBox ash shell. From here, we need to find exactly where the broken Ubuntu system lives on the hard drive.&lt;/p&gt;
&lt;p&gt;Run this command to list your partitions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;fdisk -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scan the output for your main Linux filesystem. In my case, Ubuntu was sitting on &lt;code&gt;/dev/sda2&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;step-3-find-and-repair&quot;&gt;Step 3: Find and repair&lt;/h3&gt;
&lt;p&gt;Because I was forced to hard-reset the machine multiple times while the OS was locked up, the filesystem metadata was corrupted. Files were left hanging in memory, creating &quot;orphaned inodes.&quot; If you try to boot with a corrupted filesystem, Linux will panic to protect your data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Make sure your broken partition is not mounted.&lt;br&gt;
Quick check just to be safe:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;umount /dev/sda2/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's time to repair the drive. Run the ext4 filesystem check tool on your specific partition:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;e2fsck -y /dev/sda2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(&lt;em&gt;Tip&lt;/em&gt;: The &lt;code&gt;-y&lt;/code&gt; flag is crucial here. It automatically answers &quot;yes&quot; to the hundreds of prompts asking if you want to fix individual corrupted sectors. Without it, you will be holding down the 'Y' key for an eternity).&lt;/p&gt;
&lt;p&gt;Once the screen stops scrolling, you are looking for the holy grail: a message declaring your drive &lt;code&gt;/dev/sda2: clean&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sr48xz63cwl8sjz4r8tu.jpeg&quot; alt=&quot;A Linux terminal displaying the output of the `e2fsck` command successfully clearing orphaned inodes and reporting the `/dev/sda2 `filesystem as clean.&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;step-4-the-boot-menu-amp-the-investigation&quot;&gt;Step 4: The Boot Menu &amp;amp; The Investigation&lt;/h3&gt;
&lt;p&gt;With the filesystem repaired, type reboot (or hard reset again if the shell is stubborn) and unplug the USB.&lt;/p&gt;
&lt;p&gt;Do not let it boot normally. As soon as the PC turns on, spam &lt;code&gt;esc&lt;/code&gt; to bring up the GRUB boot menu. Go to Advanced options for Ubuntu and manually select an older kernel version (e.g., 6.14.x instead of the newest one).&lt;/p&gt;
&lt;p&gt;Once you are successfully booted into your desktop, open a terminal. It is time to find the killer. List all installed kernels:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;dpkg --list | grep linux-image
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you read the output carefully, you'll likely spot the culprit. Look at the two letters on the far left of the list:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ii&lt;/code&gt; means Installed and Intact (This is your stable, older kernel).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;it&lt;/code&gt; means Installed, Triggers pending (This is a half-baked, broken kernel).&lt;/p&gt;
&lt;p&gt;In my case, the system had tried to automatically update to the 6.17 kernel in the background. But a third-party module, specifically VirtualBox DKMS, failed to compile for the new kernel architecture. VirtualBox crashed, which halted the entire kernel installation halfway through, leaving my machine with an unbootable OS.&lt;/p&gt;
&lt;h3 id=&quot;step-5-the-execution-purging-the-rot-&quot;&gt;Step 5: The Execution(Purging the Rot)&lt;/h3&gt;
&lt;p&gt;Now we just need to clean up the mess and permanently delete the broken kernel so the system stops trying to default to it.&lt;/p&gt;
&lt;p&gt;First, get the blocking software out of the way (you can reinstall it later):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt remove --purge virtualbox-dkms
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, tell the package manager to unjam itself and fix any half-installed dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt --fix-broken install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, drop the hammer on the broken kernel (replace the version numbers with the broken one from your dpkg list):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt purge linux-image-6.17.0-19-generic linux-headers-6.17.0-19-generic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, sweep up the orphaned packages and update your boot menu to lock in your stable kernel as the new default:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt autoremove
sudo update-grub
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All good ✨&lt;/p&gt;
&lt;h3 id=&quot;a-little-note-&quot;&gt;A little note...&lt;/h3&gt;
&lt;p&gt;Isn't it funny how things completely falling apart is usually the best way to figure out how they actually work?&lt;/p&gt;
&lt;p&gt;But hey, in case you found yourself in this situation, found your way here, and this still doesn't work... you should probably just delete that OS  or contact your local AI agent.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/15509/surviving-a-kernel-panic-my-ubuntu-war-story</guid>
<pubDate>Wed, 22 Apr 2026 11:08:36 +0000</pubDate>
</item>
<item>
<title>I Ported ComfyUI Desktop to Ubuntu 26.04  1 a day till I run out!</title>
<link>https://coderlegion.com/14844/i-ported-comfyui-desktop-to-ubuntu-26-04-1-a-day-till-i-run-out</link>
<description>&lt;hr&gt;
&lt;h3 id=&quot;and-another-one-&quot;&gt;And Another One!&lt;/h3&gt;
&lt;hr&gt;
&lt;h3 id=&quot;another-desktop-app-ported-to-linux-that-was-not-available-i-will-keep-going-until-release-day-or-i-run-out-of-apps-to-port-let-me-know-what-one-you-would-like-to-see-next-&quot;&gt;Another Desktop App ported to Linux That was not available. I will Keep going until release day or I run out of apps to port. Let me Know what one you would like to see next!&lt;/h3&gt;
&lt;p&gt;The official ComfyUI Desktop app ships for Windows and macOS.&lt;/p&gt;
&lt;p&gt;I wanted it on Ubuntu 26.04.&lt;/p&gt;
&lt;p&gt;So I ported it.&lt;/p&gt;
&lt;p&gt;Not a web wrapper. Not a fake launcher. Not a shell script around a browser tab.&lt;/p&gt;
&lt;p&gt;The actual desktop app.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-goal&quot;&gt;The Goal&lt;/h3&gt;
&lt;p&gt;I had two requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;build the real app on Linux&lt;/li&gt;
&lt;li&gt;make it usable on a machine that already runs ComfyUI normally&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That second one mattered more than people think.&lt;/p&gt;
&lt;p&gt;If a Linux user already has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a real &lt;code&gt;~/ComfyUI&lt;/code&gt; install&lt;/li&gt;
&lt;li&gt;a running server on &lt;code&gt;127.0.0.1:8188&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;then the desktop app should not pretend the machine is blank and try to push a fresh install into &lt;code&gt;~/Documents/ComfyUI&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is bad product behavior, even if the package technically launches.&lt;/p&gt;
&lt;h3 id=&quot;the-starting-point&quot;&gt;The Starting Point&lt;/h3&gt;
&lt;p&gt;The upstream repo is here:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Comfy-Org/desktop&quot;&gt;https://github.com/Comfy-Org/desktop&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At first glance it looks cross-platform enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Electron app&lt;/li&gt;
&lt;li&gt;bundled frontend assets&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uv&lt;/code&gt; bootstrap&lt;/li&gt;
&lt;li&gt;asset build pipeline&lt;/li&gt;
&lt;li&gt;electron-builder config with Linux bits already half-present&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But “half-present” is the dangerous state.&lt;/p&gt;
&lt;p&gt;That usually means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;some paths exist&lt;/li&gt;
&lt;li&gt;some assets exist&lt;/li&gt;
&lt;li&gt;some packaging config exists&lt;/li&gt;
&lt;li&gt;and the app still does not actually ship on Linux&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which is exactly what was going on here.&lt;/p&gt;
&lt;h3 id=&quot;the-real-problems&quot;&gt;The Real Problems&lt;/h3&gt;
&lt;p&gt;There were a few distinct failures.&lt;/p&gt;
&lt;h4 id=&quot;1-linux-was-gated-out-of-asset-bootstrap-flow&quot;&gt;1. Linux was gated out of asset/bootstrap flow&lt;/h4&gt;
&lt;p&gt;The app would build fine for supported platforms, but Linux was not being carried all the way through asset setup and build verification.&lt;/p&gt;
&lt;p&gt;That meant some Linux artifacts were never properly prepared even though the source tree looked close.&lt;/p&gt;
&lt;h4 id=&quot;2-packaged-builds-were-missing-desktop-ui&quot;&gt;2. Packaged builds were missing &lt;code&gt;desktop-ui&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;This was the first hard packaging bug.&lt;/p&gt;
&lt;p&gt;The app launched, then died with a file-not-found error because the packaged build did not contain the desktop onboarding UI bundle.&lt;/p&gt;
&lt;p&gt;That is the kind of bug that makes a port look dead before you even get to runtime behavior.&lt;/p&gt;
&lt;h4 id=&quot;3-appimage-hit-electron-sandbox-issues&quot;&gt;3. AppImage hit Electron sandbox issues&lt;/h4&gt;
&lt;p&gt;This is the standard Linux Electron tax.&lt;/p&gt;
&lt;p&gt;The AppImage mounted fine, but Chromium tripped over the sandbox helper and aborted before the app could become useful.&lt;/p&gt;
&lt;p&gt;I did not waste time pretending AppImage was the right hill to die on.&lt;/p&gt;
&lt;p&gt;For Ubuntu, &lt;code&gt;.deb&lt;/code&gt; was the better target.&lt;/p&gt;
&lt;h4 id=&quot;4-linux-runtime-assumptions-were-wrong&quot;&gt;4. Linux runtime assumptions were wrong&lt;/h4&gt;
&lt;p&gt;The app still had platform checks and install behavior that were effectively “Windows/macOS first, Linux later.”&lt;/p&gt;
&lt;p&gt;A Linux port cannot just compile. It has to stop acting like Linux is unsupported at runtime.&lt;/p&gt;
&lt;h4 id=&quot;5-existing-comfy-installs-were-ignored&quot;&gt;5. Existing Comfy installs were ignored&lt;/h4&gt;
&lt;p&gt;This was the most important UX issue.&lt;/p&gt;
&lt;p&gt;On my machine, ComfyUI was already installed and already running in the browser.&lt;/p&gt;
&lt;p&gt;The desktop app still tried to start from scratch and push a managed install path into &lt;code&gt;Documents&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That is technically launchable, but functionally wrong.&lt;/p&gt;
&lt;p&gt;So I fixed it.&lt;/p&gt;
&lt;h3 id=&quot;what-i-changed&quot;&gt;What I Changed&lt;/h3&gt;
&lt;p&gt;I split the work into build fixes, runtime fixes, and “make it behave like a sane Linux app” fixes.&lt;/p&gt;
&lt;h4 id=&quot;build-and-packaging-fixes&quot;&gt;Build and packaging fixes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;enabled Linux asset bootstrap&lt;/li&gt;
&lt;li&gt;enabled Linux build verification&lt;/li&gt;
&lt;li&gt;packaged the missing &lt;code&gt;desktop-ui&lt;/code&gt; bundle&lt;/li&gt;
&lt;li&gt;switched the Linux target to &lt;code&gt;.deb&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;runtime-fixes&quot;&gt;Runtime fixes&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;added Linux config/reset path handling&lt;/li&gt;
&lt;li&gt;made Linux hardware validation non-fatal&lt;/li&gt;
&lt;li&gt;added fallback behavior for missing compiled requirements&lt;/li&gt;
&lt;li&gt;added Linux-safe Electron startup defaults&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;existing-install-behavior&quot;&gt;Existing-install behavior&lt;/h4&gt;
&lt;p&gt;This was the good part.&lt;/p&gt;
&lt;p&gt;I changed the app so it now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prefers an existing &lt;code&gt;~/ComfyUI&lt;/code&gt; directory as the default base path&lt;/li&gt;
&lt;li&gt;detects a live local Comfy server on &lt;code&gt;127.0.0.1:8188&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;persists external-server mode into desktop config&lt;/li&gt;
&lt;li&gt;skips the desktop-managed venv validation path when attaching to that external server&lt;/li&gt;
&lt;li&gt;opens the frontend against the already-running instance instead of forcing first-run install flow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last bit is what makes the port feel real instead of merely “bootable.”&lt;/p&gt;
&lt;h3 id=&quot;why-deb-won&quot;&gt;Why &lt;code&gt;.deb&lt;/code&gt; Won&lt;/h3&gt;
&lt;p&gt;I know someone will ask why I did not just ship AppImage.&lt;/p&gt;
&lt;p&gt;Because Ubuntu users need something that works, not something ideologically pure.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;.deb&lt;/code&gt; route gave me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;stable installation under &lt;code&gt;/opt/ComfyUI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;normal desktop integration&lt;/li&gt;
&lt;li&gt;no AppImage sandbox headache&lt;/li&gt;
&lt;li&gt;a clean launcher path&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Linux, packaging format is not theology. It is operational reality.&lt;/p&gt;
&lt;h3 id=&quot;what-the-app-does-now&quot;&gt;What the App Does Now&lt;/h3&gt;
&lt;p&gt;On an Ubuntu 26.04 machine with an existing Comfy setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;launch the desktop app&lt;/li&gt;
&lt;li&gt;it detects &lt;code&gt;~/ComfyUI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;it sees the live server on &lt;code&gt;127.0.0.1:8188&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;it stores that as the desktop install state&lt;/li&gt;
&lt;li&gt;it attaches instead of onboarding&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the behavior I wanted from the start.&lt;/p&gt;
&lt;h3 id=&quot;commands-i-actually-used&quot;&gt;Commands I Actually Used&lt;/h3&gt;
&lt;p&gt;Install dependencies:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;corepack enable
corepack yarn install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bootstrap app assets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;corepack yarn make:assets
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;corepack yarn typecheck
corepack yarn vitest run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Build the package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;corepack yarn make
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install the package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install ./dist/ComfyUI-0.8.30-amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-nice-surprise&quot;&gt;The Nice Surprise&lt;/h3&gt;
&lt;p&gt;The upstream repo was not hopeless.&lt;/p&gt;
&lt;p&gt;This was not one of those situations where “Linux support” secretly meant rewriting the app from scratch.&lt;/p&gt;
&lt;p&gt;Most of the work was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;finishing incomplete Linux paths&lt;/li&gt;
&lt;li&gt;fixing packaging omissions&lt;/li&gt;
&lt;li&gt;picking the right distribution target&lt;/li&gt;
&lt;li&gt;making startup logic respect an existing local Comfy workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is still real work, but it is not fantasy.&lt;/p&gt;
&lt;h3 id=&quot;the-useful-lesson&quot;&gt;The Useful Lesson&lt;/h3&gt;
&lt;p&gt;A port is not finished when the window appears.&lt;/p&gt;
&lt;p&gt;A port is finished when the app behaves correctly on the target machine.&lt;/p&gt;
&lt;p&gt;For this one, the difference was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not just “ComfyUI Desktop launches on Ubuntu”&lt;/li&gt;
&lt;li&gt;but “ComfyUI Desktop understands an Ubuntu user who already runs ComfyUI”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is the difference between a demo and a port.&lt;/p&gt;
&lt;h3 id=&quot;upstream-and-source&quot;&gt;Upstream and Source&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Upstream desktop app: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Comfy-Org/desktop&quot;&gt;https://github.com/Comfy-Org/desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ComfyUI core: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comfyanonymous/ComfyUI&quot;&gt;https://github.com/comfyanonymous/ComfyUI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;my-repo&quot;&gt;My Repo&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/johnohhh1/comfyui-desktop-port-linux.git&quot;&gt;https://github.com/johnohhh1/comfyui-desktop-port-linux.git&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/johnohhh1/comfyui-desktop-port-linux.git

&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14844/i-ported-comfyui-desktop-to-ubuntu-26-04-1-a-day-till-i-run-out</guid>
<pubDate>Wed, 15 Apr 2026 03:17:57 +0000</pubDate>
</item>
<item>
<title>Here is the Kimi Desktop on Ubuntu 26.04 noone asked for..... Because I am creating 1 desktop a day</title>
<link>https://coderlegion.com/14718/here-is-the-kimi-desktop-on-ubuntu-26-04-noone-asked-for-because-i-am-creating-1-desktop-a-day</link>
<description>&lt;hr&gt;
&lt;p&gt;I decided I am going to create 1 desktop app a day for the new Ubuntu release because noone ships linux. It's crazy they expect us to dev in linux but ship for windows and mac but you know what ? I am tired of bouncing back and forth! Linux for this windows for that ohhh check in on the kids switch to mac. It;s enough. So here is app 5 (if you dont count the Voicebox I made earlier today because their junk was busted and their instructions were just build it from source... Yeah thanks Chad...Here ya go. &quot;Kimi Desktop on Ubuntu 26.04: Fixing the Broken .deb with Tauri v2&quot;&lt;/p&gt;
&lt;h3 id=&quot;-the-official-kimi-desktop-app-can-t-install-on-ubuntu-24-04-because-it-depends-on-a-removed-library-here-s-how-i-rebuilt-it-with-tauri-v2-and-pake-v3-to-fix-it-&quot;&gt;&quot;The official Kimi desktop app can't install on Ubuntu 24.04+ because it depends on a removed library. Here's how I rebuilt it with Tauri v2 and Pake v3 to fix it.&quot;&lt;/h3&gt;
&lt;hr&gt;
&lt;p&gt;You install the official Kimi desktop &lt;code&gt;.deb&lt;/code&gt;, fire &lt;code&gt;sudo dpkg -i&lt;/code&gt;, and boom:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dpkg: dependency problems prevent configuration of kimi:
 kimi depends on libwebkit2gtk-4.0-37; however:
  Package libwebkit2gtk-4.0-37 is not installed.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That library doesn't exist on Ubuntu 24.04, let alone 26.04. It was removed from the repos over a year ago. The official &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kimi-moonshot/kimi-moonshot&quot;&gt;Kimi desktop package&lt;/a&gt; is built on Tauri v1, which hard-depends on &lt;code&gt;libwebkit2gtk-4.0.so.37&lt;/code&gt; — a library that shipped with webkit2gtk 4.0, superseded by 4.1 and then dropped entirely.&lt;/p&gt;
&lt;p&gt;So the app is just... broken on any modern Ubuntu. Here's how I fixed it.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-in-one-sentence&quot;&gt;The problem in one sentence&lt;/h3&gt;
&lt;p&gt;Tauri v1 → &lt;code&gt;libwebkit2gtk-4.0&lt;/code&gt; → removed from Ubuntu 24.04+ → &lt;code&gt;dpkg&lt;/code&gt; fails.&lt;/p&gt;
&lt;h3 id=&quot;the-fix-rebuild-with-tauri-v2&quot;&gt;The fix: rebuild with Tauri v2&lt;/h3&gt;
&lt;p&gt;Tauri v2 links against &lt;code&gt;libwebkit2gtk-4.1&lt;/code&gt;, which &lt;em&gt;is&lt;/em&gt; the version shipped in Ubuntu 24.04 and 26.04. So the fix is straightforward: rebuild the app with Tauri v2 instead of v1.&lt;/p&gt;
&lt;p&gt;I used &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/tw93/Pake&quot;&gt;Pake&lt;/a&gt; v3, which wraps any web app into a native desktop app using Tauri under the hood. One build script, one config file, and you get a &lt;code&gt;.deb&lt;/code&gt; that actually installs.&lt;/p&gt;
&lt;h3 id=&quot;what-you-get&quot;&gt;What you get&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Tauri v2 runtime&lt;/strong&gt; — Links against &lt;code&gt;libwebkit2gtk-4.1&lt;/code&gt;, the one Ubuntu actually ships&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OAuth / SSO&lt;/strong&gt; — &lt;code&gt;--new-window&lt;/code&gt; flag means Google sign-in works in-app instead of being blocked&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;System tray&lt;/strong&gt; — Desktop integration that works&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1200x780 window&lt;/strong&gt; — Matches the original Kimi desktop dimensions&lt;/p&gt;
&lt;h3 id=&quot;rebuild-it-yourself&quot;&gt;Rebuild it yourself&lt;/h3&gt;
&lt;p&gt;Prerequisites — Rust, Node, and the usual GTK/webkit dev packages:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Rust &amp;gt;= 1.85
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Node.js &amp;gt;= 22 — use nvm, brew, whatever you prefer

# Build deps
sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev \
  libayatana-appindicator3-dev librsvg2-dev

# Pake CLI
npm install -g pake-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it's one command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;./build.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.deb&lt;/code&gt; lands in &lt;code&gt;dist/&lt;/code&gt;. Install it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dpkg -i dist/kimi_1.0.0_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done. Kimi runs natively on Ubuntu 26.04 with no missing libraries.&lt;/p&gt;
&lt;h3 id=&quot;the-config-that-makes-it-work&quot;&gt;The config that makes it work&lt;/h3&gt;
&lt;p&gt;Everything lives in &lt;code&gt;config/pake.json&lt;/code&gt;. The important bits:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;windows&quot;: [{
    &quot;url&quot;: &quot;https://kimi.moonshot.cn&quot;,
    &quot;new_window&quot;: true,
    &quot;width&quot;: 1200,
    &quot;height&quot;: 780
  }],
  &quot;user_agent&quot;: {
    &quot;linux&quot;: &quot;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two things that matter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;new_window: true&lt;/code&gt;&lt;/strong&gt; — Without this, OAuth popups (Google sign-in, etc.) get blocked by the webview's navigation policy. This flag tells Pake/Tauri to open them in a new window instead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;user_agent.linux&lt;/code&gt;&lt;/strong&gt; — Spoofing a Chrome UA because some OAuth providers reject webview user agents.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-not-just-use-the-web-app-in-a-browser-&quot;&gt;Why not just use the web app in a browser?&lt;/h3&gt;
&lt;p&gt;Fair question. A native desktop app gives you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Alt-Tab separation&lt;/strong&gt; — Kimi isn't buried among 40 browser tabs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System tray&lt;/strong&gt; — Quick access, stays running in the background&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Own window chrome&lt;/strong&gt; — Feels like an app, not a tab&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smaller memory footprint&lt;/strong&gt; — Tauri uses the system webview, not a bundled Electron instance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-repo&quot;&gt;The repo&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/johnohhh1/kimi-app&quot;&gt;github.com/johnohhh1/kimi-app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clone it, build it, install it. If you're on Ubuntu 24.04+ and want Kimi as a desktop app, this is currently the only way that works.&lt;/p&gt;
&lt;h3 id=&quot;uninstall&quot;&gt;Uninstall&lt;/h3&gt;
&lt;p&gt;If you need to remove it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dpkg -r kimi
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Kimi is a product of &lt;a rel=&quot;nofollow&quot; href=&quot;https://moonshot.cn&quot;&gt;Moonshot AI&lt;/a&gt;. This project uses the open-source &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/tw93/Pake&quot;&gt;Pake&lt;/a&gt; tool (MIT license) to wrap the Kimi web interface as a native desktop application.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/14718/here-is-the-kimi-desktop-on-ubuntu-26-04-noone-asked-for-because-i-am-creating-1-desktop-a-day</guid>
<pubDate>Tue, 14 Apr 2026 03:17:32 +0000</pubDate>
</item>
<item>
<title>I Ported the Ollama Desktop App to Linux Just in Time for Ubuntu 26.04 LTS</title>
<link>https://coderlegion.com/14525/i-ported-the-ollama-desktop-app-to-linux-just-in-time-for-ubuntu-26-04-lts</link>
<description>&lt;p&gt;Ubuntu 26.04 LTS drops this week. I've been running the RC for months. And I think it's going to be the release that finally pulls a meaningful chunk of people off Windows — especially AI-curious developers who are tired of the telemetry, the forced updates, and the general feeling that Microsoft is treating them like a product.&lt;/p&gt;
&lt;p&gt;So I've been on a mission: take the cool AI tooling that still assumes Windows or macOS, and make it work natively on Linux.&lt;/p&gt;
&lt;p&gt;This week's project: &lt;strong&gt;the official Ollama desktop app&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-1&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://ollama.com&quot;&gt;Ollama&lt;/a&gt; is incredible. Local LLMs, dead simple, fast. But their desktop app — the polished GTK-style chat UI with model management, file attachments, settings, chat history — &lt;strong&gt;officially ships for Windows and macOS only&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Every single Go file in the &lt;code&gt;app/&lt;/code&gt; directory of &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ollama/ollama&quot;&gt;ollama/ollama&lt;/a&gt; starts with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;//go:build windows || darwin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux users are left running &lt;code&gt;ollama serve&lt;/code&gt; and pointing a browser at &lt;code&gt;localhost:11434&lt;/code&gt;. It works, but it's not the experience.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-discovery&quot;&gt;The Discovery&lt;/h3&gt;
&lt;p&gt;Before writing a single line of code, I did what any good engineer does: I read the source.&lt;/p&gt;
&lt;p&gt;The Ollama desktop app is built on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/webview/webview&quot;&gt;webview&lt;/a&gt; — a lightweight C++ library that wraps the platform's native browser engine. On Windows it uses WebView2 (Edge). On macOS it uses WebKit. And on Linux?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// From webview.h, line ~400:
#if defined(WEBVIEW_GTK)
// Full GTK3 + WebKit2GTK implementation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The Linux support was already there.&lt;/strong&gt; Fully implemented. Sitting in the header file, waiting. The Go build tags were the only thing blocking it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-port&quot;&gt;The Port&lt;/h3&gt;
&lt;p&gt;Six new files. A patch. About 350 lines of Go.&lt;/p&gt;
&lt;h4 id=&quot;1-unlock-the-build-tags&quot;&gt;1. Unlock the build tags&lt;/h4&gt;
&lt;p&gt;~40 files needed one line changed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;// before
//go:build windows || darwin

// after  
//go:build windows || darwin || linux
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-wire-up-the-cgo-flags&quot;&gt;2. Wire up the CGo flags&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;app/webview/webview.go&lt;/code&gt; needed Linux-specific build flags to find GTK and WebKit:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;#cgo linux CXXFLAGS: -DWEBVIEW_GTK -std=c++14
#cgo linux LDFLAGS: -ldl
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: Ubuntu 26.04 ships &lt;code&gt;webkit2gtk-4.1&lt;/code&gt; — not &lt;code&gt;4.0&lt;/code&gt;. If you're targeting older distros, check your version.&lt;/p&gt;
&lt;h4 id=&quot;3-platform-files&quot;&gt;3. Platform files&lt;/h4&gt;
&lt;p&gt;The app architecture expects platform-specific implementations of a handful of interfaces. I wrote six &lt;code&gt;_linux.go&lt;/code&gt; files:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;app_linux.go&lt;/code&gt;&lt;/strong&gt; — GTK window management via CGo, lockfile-based single-instance enforcement, signal cleanup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;func handleExistingInstance(startHidden bool) {
    f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o644)
    if err != nil {
        slog.Info(&quot;another ollama-app instance detected, exiting&quot;)
        os.Exit(0)
    }
    f.Close()
    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
        &amp;lt;-c
        removeLock()
    }()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;server_linux.go&lt;/code&gt;&lt;/strong&gt; — The app normally spawns the Ollama daemon itself. On Linux, systemd handles that. So this file is just a health-checker that pings &lt;code&gt;localhost:11434&lt;/code&gt; every 30 seconds and logs if it's unreachable. Never spawns a process.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;updater_linux.go&lt;/code&gt;&lt;/strong&gt; — All no-ops. systemd and apt manage updates on Linux.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;filepicker_linux.go&lt;/code&gt;&lt;/strong&gt; — File/folder dialogs via &lt;code&gt;zenity&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;func selectDirectory(title string) (string, error) {
    out, err := exec.Command(&quot;zenity&quot;, &quot;--file-selection&quot;, &quot;--directory&quot;, &quot;--title=&quot;+title).Output()
    if err != nil {
        return &quot;&quot;, err
    }
    return strings.TrimSpace(string(out)), nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;dialog/dlgs_linux.go&lt;/code&gt;&lt;/strong&gt; — zenity implementations of the app's dialog interface (yes/no, info, error, file pickers).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;webview_linux.go&lt;/code&gt;&lt;/strong&gt; — The Webview struct without the Windows &lt;code&gt;menu.h&lt;/code&gt; CGo baggage, plus all the JS bindings: &lt;code&gt;ready&lt;/code&gt;, &lt;code&gt;close&lt;/code&gt;, &lt;code&gt;drag&lt;/code&gt;, &lt;code&gt;doubleClick&lt;/code&gt;, &lt;code&gt;zoomIn&lt;/code&gt;, &lt;code&gt;zoomOut&lt;/code&gt;, &lt;code&gt;selectFiles&lt;/code&gt;, &lt;code&gt;selectWorkingDirectory&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;4-the-one-frontend-bug&quot;&gt;4. The one frontend bug&lt;/h4&gt;
&lt;p&gt;The React sidebar had:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;{isWindows &amp;amp;&amp;amp; (
  &amp;lt;Link href=&quot;/settings&quot;&amp;gt;Settings&amp;lt;/Link&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Linux, &lt;code&gt;navigator.platform&lt;/code&gt; returns &lt;code&gt;&quot;Linux x86_64&quot;&lt;/code&gt; — so &lt;code&gt;isWindows&lt;/code&gt; is false and Settings just... doesn't appear. One-line fix:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;const isMac = navigator.platform.toLowerCase().includes(&quot;mac&quot;);

{!isMac &amp;amp;&amp;amp; (
  &amp;lt;Link href=&quot;/settings&quot;&amp;gt;Settings&amp;lt;/Link&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-result&quot;&gt;The Result&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/johnohhh1/ollama-webchat-ubuntu.git
cd ollama-webchat-ubuntu
bash build.sh
sudo cp build/ollama-app /usr/local/bin/ollama-app
ollama-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A native GTK3 window. WebKit2GTK rendering the full React SPA. Chat history in SQLite. File attachments. Model management. Settings. Dark mode. Wayland-native — no XWayland needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-nvidia-gotcha&quot;&gt;The NVIDIA gotcha&lt;/h3&gt;
&lt;p&gt;Running an RTX 5070 (Blackwell) with the proprietary driver? You'll see a wall of EGL warnings on startup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;libEGL warning: egl: failed to create dri2 screen
libEGL warning: pci id for fd 17: 10de:2f04, driver (null)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ignore them.&lt;/strong&gt; There's no open-source Mesa driver for Blackwell yet — WebKit probes Mesa EGL, fails, and falls back to Cairo software rendering for the compositor. For a chat UI it's completely imperceptible. Page layout and text were always CPU-rendered anyway.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-ubuntu-26-04-changes-things&quot;&gt;Why Ubuntu 26.04 changes things&lt;/h3&gt;
&lt;p&gt;26.04 LTS is the first Ubuntu release with X11 removed entirely — Wayland only. GNOME 50. The desktop finally feels like a first-class target, not an afterthought.&lt;/p&gt;
&lt;p&gt;At the same time, the local AI tooling ecosystem has exploded. Ollama, LM Studio, Open WebUI, llama.cpp — these tools are genuinely good, and they mostly work on Linux. The desktop app experience is just lagging behind.&lt;/p&gt;
&lt;p&gt;That's the gap I'm trying to close. If you're a developer making the jump from Windows to Ubuntu 26.04 this week, you shouldn't have to give up your tools.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;repo&quot;&gt;Repo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/johnohhh1/ollama-webchat-ubuntu&quot;&gt;github.com/johnohhh1/ollama-webchat-ubuntu&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;PRs welcome — especially if you test on Fedora, Arch, or Ubuntu 24.04. And if you're building other AI tools that are stuck behind Windows/macOS build tags, the pattern here is reusable.&lt;/p&gt;
&lt;p&gt;The C library usually already supports Linux. The build tags are almost always the only thing in the way.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14525/i-ported-the-ollama-desktop-app-to-linux-just-in-time-for-ubuntu-26-04-lts</guid>
<pubDate>Sat, 11 Apr 2026 22:50:53 +0000</pubDate>
</item>
<item>
<title>Run the Real ChatGPT Desktop App on Ubuntu Linux (Not a Wrapper)</title>
<link>https://coderlegion.com/14372/run-the-real-chatgpt-desktop-app-on-ubuntu-linux-not-a-wrapper</link>
<description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; OpenAI ships a Windows MSIX binary. You can unpack it, patch three platform assumptions, and run the actual official app natively on Ubuntu — same binary Windows users get. No Electron wrapper, no web view in a box.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-with-every-other-guide&quot;&gt;The Problem With Every Other Guide&lt;/h3&gt;
&lt;p&gt;Search &quot;ChatGPT desktop Ubuntu&quot; and you'll find two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Articles pointing you at &lt;code&gt;lencx/ChatGPT&lt;/code&gt; — a third-party Electron wrapper that loads chatgpt.com in a window. It's not the real app.&lt;/li&gt;
&lt;li&gt;Articles pointing you at other wrappers doing the same thing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These work, but you're getting a community-built shell around a website — not the app itself. The real ChatGPT Desktop (the one in the Windows Store) has features, update hooks, and auth flows that wrapper apps can't replicate cleanly.&lt;/p&gt;
&lt;p&gt;This guide unpacks the official binary and runs it on Ubuntu 26.04. Tested on kernel 7.0.0, RTX 5070, NVIDIA 580/CUDA 13.0, both X11 and Wayland via XWayland.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-the-script-actually-does&quot;&gt;What the Script Actually Does&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Extracts the &lt;code&gt;x64&lt;/code&gt; MSIX from the official &lt;code&gt;.msixbundle&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pulls out the official &lt;code&gt;app.asar&lt;/code&gt; (the real app logic)&lt;/li&gt;
&lt;li&gt;Patches three platform assumptions so it boots on Linux:&lt;ul&gt;
&lt;li&gt;Routes the platform chooser through the macOS-style implementation (Linux is close enough)&lt;/li&gt;
&lt;li&gt;Disables macOS-only &lt;code&gt;setVibrancy()&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;Skips the macOS &lt;code&gt;ioreg&lt;/code&gt; device ID path&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stages Linux Electron around the official app resources&lt;/li&gt;
&lt;li&gt;Packages everything as a proper &lt;code&gt;.deb&lt;/code&gt; — &lt;code&gt;chatgpt-desktop-native&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You end up with a system package you can install, update, and uninstall like anything else.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;h4 id=&quot;system-packages&quot;&gt;System packages&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install -y dpkg-dev nodejs python3 file
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;local-electron-tooling&quot;&gt;Local Electron tooling&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir ~/chatgpt-windows-deb &amp;amp;&amp;amp; cd ~/chatgpt-windows-deb
npm install electron @electron/asar --no-save
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;get-the-official-msix-bundle&quot;&gt;Get the Official MSIX Bundle&lt;/h3&gt;
&lt;p&gt;You need the official Windows package from OpenAI. Download &lt;code&gt;OpenAI.ChatGPT-Desktop_&amp;lt;version&amp;gt;.Msixbundle&lt;/code&gt; from the Microsoft Store or OpenAI's distribution endpoint and drop it in your working directory.&lt;/p&gt;
&lt;p&gt;The repo includes the version current at time of writing as an example payload.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;build&quot;&gt;Build&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd ~/chatgpt-windows-deb
git clone https://github.com/johnohhh1/chatgpt_desktop_ubuntu .
npm install electron @electron/asar --no-save
./build-chatgpt-native-deb.sh --exe ./OpenAI.ChatGPT-Desktop_2026.212.2039.0.Msixbundle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output lands in &lt;code&gt;dist/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dist/chatgpt-desktop-native_2026.212.2039.0_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;install&quot;&gt;Install&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install ./dist/chatgpt-desktop-native_2026.212.2039.0_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rebuilding without a version bump? Force refresh:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install --reinstall ./dist/chatgpt-desktop-native_2026.212.2039.0_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;register-auth-callback-handlers&quot;&gt;Register Auth Callback Handlers&lt;/h3&gt;
&lt;p&gt;The package installs a helper that registers your desktop session as the handler for the &lt;code&gt;chatgpt:&lt;/code&gt; and &lt;code&gt;chatgpt-alt:&lt;/code&gt; URL schemes (used for the login flow):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chatgpt-desktop-native-register
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verify it took:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;xdg-mime query default x-scheme-handler/chatgpt
xdg-mime query default x-scheme-handler/chatgpt-alt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both should return &lt;code&gt;chatgpt-desktop-native.desktop&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;launch&quot;&gt;Launch&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chatgpt-desktop-native
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The desktop entry sets the WM class to &lt;code&gt;electron&lt;/code&gt; so GNOME binds the running window to the ChatGPT icon instead of the generic gear icon.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;known-quirks&quot;&gt;Known Quirks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The terminal will print Electron/NVIDIA/VA-API noise. This is normal — ignore it.&lt;/li&gt;
&lt;li&gt;The success signal is functional login and working chat, not a clean terminal.&lt;/li&gt;
&lt;li&gt;If GNOME still shows the generic icon after first launch: close the app fully and relaunch once. Stubborn shell? Log out and back in.&lt;/li&gt;
&lt;li&gt;If OpenAI updates the Windows app significantly, the patch targets in &lt;code&gt;build-chatgpt-native-deb.sh&lt;/code&gt; may need updating. PR welcome.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;reproducing-on-another-machine&quot;&gt;Reproducing on Another Machine&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Clone the repo to the target machine&lt;/li&gt;
&lt;li&gt;Drop a real ChatGPT &lt;code&gt;.msix&lt;/code&gt;, &lt;code&gt;.msixbundle&lt;/code&gt;, &lt;code&gt;.appx&lt;/code&gt;, or &lt;code&gt;.appxbundle&lt;/code&gt; in the directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm install electron @electron/asar --no-save&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;./build-chatgpt-native-deb.sh --exe &amp;lt;your-payload&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install the generated &lt;code&gt;.deb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;chatgpt-desktop-native-register&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Launch &lt;code&gt;chatgpt-desktop-native&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;repo-1&quot;&gt;Repo&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/johnohhh1/chatgpt_desktop_ubuntu&quot;&gt;github.com/johnohhh1/chatgpt_desktop_ubuntu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Issues and PRs open. If a new MSIX version breaks the patches, open an issue with the version string and I'll update the patch targets.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Tested on Ubuntu 26.04 &quot;Noble&quot; with kernel 7.0.0-10, RTX 5070, NVIDIA driver 580, CUDA 13.0.&lt;/em&gt;&lt;strong&gt;strong text&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14372/run-the-real-chatgpt-desktop-app-on-ubuntu-linux-not-a-wrapper</guid>
<pubDate>Thu, 09 Apr 2026 20:58:42 +0000</pubDate>
</item>
<item>
<title>Fixing Google Antigravity Pro Authentication on Windows 10 + WSL2</title>
<link>https://coderlegion.com/10207/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2</link>
<description>&lt;p&gt;Google Antigravity is Google's new agent-first IDE that promises powerful AI-assisted development. However, getting the Pro features to work on Windows 10 with WSL2 (Ubuntu 24.04.3 LTS) can be challenging due to authentication issues. This comprehensive guide will walk you through every step needed to get your Pro/Ultra subscription working seamlessly.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-2&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;When running Antigravity in WSL2, the authentication handshake between your Linux terminal and Windows browser often breaks or times out. This happens because WSL2 is a virtualized environment, and the redirect token struggles to find its way back to the WSL instance.&lt;/p&gt;
&lt;p&gt;Common symptoms include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Browser opens but IDE stays on loading screen&lt;/li&gt;
&lt;li&gt;Browser Authentication is successful but the Agent Panel still says &quot;Reqire Authentication&quot; (or something similar), even though the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen shows Account as already signed in.&lt;/li&gt;
&lt;li&gt;&quot;Invalid Token&quot; or &quot;Login Expired&quot; errors&lt;/li&gt;
&lt;li&gt;&quot;Not Eligible&quot; or &quot;Unauthorized&quot; messages&lt;/li&gt;
&lt;li&gt;Empty responses when running &lt;code&gt;agy&lt;/code&gt; commands&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;prerequisites-1&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Before starting, ensure you have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows 10 with WSL2 installed&lt;/li&gt;
&lt;li&gt;Ubuntu 24.04.3 LTS (or similar)&lt;/li&gt;
&lt;li&gt;Google Antigravity installed on Windows&lt;/li&gt;
&lt;li&gt;A Google AI Pro or Ultra subscription using a personal Gmail account.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;step-1-fix-the-silent-login-loop&quot;&gt;Step 1: Fix the Silent Login Loop&lt;/h3&gt;
&lt;p&gt;The first issue to address is WSL2's default NAT networking, which can hide the local ports Antigravity uses to listen for login signals.&lt;/p&gt;
&lt;h4 id=&quot;enable-mirrored-networking&quot;&gt;Enable Mirrored Networking&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;On Windows, navigate to &lt;code&gt;%USERPROFILE%&lt;/code&gt; (e.g., &lt;code&gt;C:\Users\YourName&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Create or edit a file named &lt;code&gt;.wslconfig&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add the following configuration:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[wsl2]
networkingMode=mirrored
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Restart WSL by opening PowerShell as Administrator and running:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;wsl --shutdown
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may start getting this message on WSL Startup, especially on Windows 10 (shouldn't theoretically appear on Windows 11):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;wsl: Mirrored networking mode is not supported: Windows version 19045.6466 does not have the required features.
Falling back to NAT networking.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In which case, you should remove the unsupported setting from your global config to get rid of the warning every time you boot/reboot and open Ubuntu, as WSL2 cannot enable this mode and defaults back to the standard NAT (Network Address Translation).&lt;/p&gt;
&lt;p&gt;Instead, you can try the following config to ensure that Agents always reliably reach local addresses (Supported on Windows 10):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[wsl2]
localhostForwarding=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add or ensure these lines exist in &lt;code&gt;%USERPROFILE%\.wslconfig&lt;/code&gt;. Save and run &lt;code&gt;wsl --shutdown&lt;/code&gt; in your terminal.&lt;/p&gt;
&lt;h3 id=&quot;step-2-synchronize-your-system-clock&quot;&gt;Step 2: Synchronize Your System Clock&lt;/h3&gt;
&lt;p&gt;Google's OAuth 2.0 authentication is highly sensitive to time drift. WSL2 clocks often drift when a laptop sleeps (like in my case), causing tokens to be rejected as expired.&lt;/p&gt;
&lt;p&gt;Run this in your Ubuntu terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ntpdate time.windows.com

# If you don't have ntpdate installed:
sudo apt install util-linux-extra &amp;amp;&amp;amp; sudo hwclock -s
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-3-try-authentication-again&quot;&gt;Step 3: Try Authentication again&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;If GUI login continues to fail, manually Sign out from your account in the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen.&lt;/li&gt;
&lt;li&gt;Close and Restart the IDE.&lt;/li&gt;
&lt;li&gt;Click on the &lt;code&gt;Google Sign in&lt;/code&gt; button in the top right and Authorize your Google AI Pro account.&lt;/li&gt;
&lt;li&gt;Close the Antigravity Sign-in confirmation page.&lt;/li&gt;
&lt;li&gt;If the issue persists, repeat the first step and continue reading below.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;step-4-verify-account-eligibility&quot;&gt;Step 4: Verify Account Eligibility&lt;/h3&gt;
&lt;p&gt;Many authentication failures are actually due to account restrictions:&lt;/p&gt;
&lt;h4 id=&quot;account-type&quot;&gt;Account Type&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Personal accounts&lt;/strong&gt; (&lt;code&gt;@gmail.com&lt;/code&gt;) have the highest success rate&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Workspace accounts&lt;/strong&gt; may require admin approval for &quot;Experimental Google AI Services&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;regional-matching&quot;&gt;Regional Matching&lt;/h4&gt;
&lt;p&gt;Ensure your Windows Region, Google Account Region, and IP address (no VPN) all match. Mismatched regions can cause silent authentication failures.&lt;/p&gt;
&lt;h3 id=&quot;step-5-fix-missing-wsl-extension-scripts&quot;&gt;Step 5: Fix Missing WSL Extension Scripts&lt;/h3&gt;
&lt;p&gt;A known bug in early Antigravity builds causes missing WSL helper scripts. Here's how to fix it:&lt;/p&gt;
&lt;h4 id=&quot;manual-script-extraction&quot;&gt;Manual Script Extraction&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Download the &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl&quot;&gt;Remote - WSL extension&lt;/a&gt; from the VS Code Marketplace&lt;/li&gt;
&lt;li&gt;Click &quot;Download VSIX&quot; (downloads a &lt;code&gt;.vsix&lt;/code&gt; file) if in IDE extensions panel or use a tool like &lt;a rel=&quot;nofollow&quot; href=&quot;https://cypherpunksamurai.github.io/vsix-downloader-webui/&quot;&gt;VSIX Downloader&lt;/a&gt; for web download.&lt;/li&gt;
&lt;li&gt;Rename the file from &lt;code&gt;.vsix&lt;/code&gt; to &lt;code&gt;.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open the zip and navigate to &lt;code&gt;extension/scripts/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copy all files (especially &lt;code&gt;wslCode.sh&lt;/code&gt; and &lt;code&gt;wslDownload.sh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Paste into: &lt;code&gt;%LOCALAPPDATA%\Programs\Antigravity\resources\app\extensions\antigravity-remote-wsl\scripts\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;fix-the-launcher-id&quot;&gt;Fix the Launcher ID&lt;/h4&gt;
&lt;p&gt;Even with scripts present, you need to update the extension ID reference:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;%LOCALAPPDATA%\Programs\Antigravity\bin\&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open the &lt;code&gt;antigravity&lt;/code&gt; file in Notepad&lt;/li&gt;
&lt;li&gt;Find: &lt;code&gt;WSL_EXT_ID=&quot;ms-vscode-remote.remote-wsl&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Change to: &lt;code&gt;WSL_EXT_ID=&quot;google.antigravity-remote-wsl&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Save and restart your WSL terminal&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;test-wsl-sign-in-command-palette-method-&quot;&gt;Test WSL Sign In (Command Palette Method)&lt;/h4&gt;
&lt;p&gt;Try Authentication in WSL:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Antigravity on Windows&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Ctrl + Shift + P&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Type &quot;WSL&quot; and select &lt;strong&gt;Remote-WSL: Connect to WSL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;The bottom-left corner should show &lt;code&gt;[WSL: Ubuntu]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Try signing in from this connected state&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;step-6-create-the-wsl-to-windows-bridge&quot;&gt;Step 6: Create the WSL-to-Windows Bridge&lt;/h3&gt;
&lt;p&gt;To use &lt;code&gt;agy&lt;/code&gt; commands from your Ubuntu terminal, you need to create a proper bridge to the Windows binary.&lt;/p&gt;
&lt;h4 id=&quot;create-the-wrapper-script&quot;&gt;Create the Wrapper Script&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Create the local bin directory:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/.local/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Create the &lt;code&gt;agy&lt;/code&gt; wrapper (replace &lt;code&gt;[YOUR_WINDOWS_USERNAME]&lt;/code&gt; with your actual Windows username):&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat &amp;gt; ~/.local/bin/agy &amp;lt;&amp;lt;'EOF'
#!/usr/bin/env bash
# Robust wrapper to bridge WSL -&amp;gt; Windows CMD -&amp;gt; Antigravity
# REPLACE [YOUR_WINDOWS_USERNAME] with your actual folder name

WIN_USER=&quot;[YOUR_WINDOWS_USERNAME]&quot;
WIN_PATH=&quot;C:\\Users\\${WIN_USER}\\AppData\\Local\\Programs\\Antigravity\\bin\\antigravity.cmd&quot;

# Call via cmd.exe to ensure Windows environment is initialized
# cd to C: drive to avoid UNC path warnings
cd /mnt/c &amp;amp;&amp;amp; cmd.exe /c &quot;${WIN_PATH}&quot; &quot;$@&quot;
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Make it executable:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;chmod +x ~/.local/bin/agy
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Add to your PATH:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export PATH=&quot;$HOME/.local/bin:$PATH&quot;
echo 'export PATH=&quot;$HOME/.local/bin:$PATH&quot;' &amp;gt;&amp;gt; ~/.bashrc
source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;verify-your-windows-username&quot;&gt;Verify Your Windows Username&lt;/h4&gt;
&lt;p&gt;Before running the wrapper, verify your path:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ls /mnt/c/Users/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;WIN_USER&lt;/code&gt; variable in the script to match exactly how your Windows username folder is spelled.&lt;/p&gt;
&lt;h4 id=&quot;enable-wsl-interoperability&quot;&gt;Enable WSL Interoperability&lt;/h4&gt;
&lt;p&gt;Ensure WSL can launch Windows processes by checking &lt;code&gt;/etc/wsl.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat /etc/wsl.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it doesn't exist or is empty, create it with these settings:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;[interop]
enabled=true
appendWindowsPath=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then restart WSL with &lt;code&gt;wsl --shutdown&lt;/code&gt; from PowerShell.&lt;/p&gt;
&lt;h3 id=&quot;step-7-verify-pro-authentication&quot;&gt;Step 7: Verify Pro Authentication&lt;/h3&gt;
&lt;p&gt;Once everything is set up, verify your CLI Setup:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Check version
agy --version

# List available commands
agy --help

# Open IDE in current directory
agy .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now try signing in again to your Google account. The &lt;code&gt;Agent Panel&lt;/code&gt; should be available now, and the &lt;code&gt;Antigravity Settings &amp;gt; Account&lt;/code&gt; Screen shows Account as already signed in. Expected output for &lt;code&gt;Agent Panel&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tier:&lt;/strong&gt; Pro or Ultra&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model:&lt;/strong&gt; Gemini 3 Pro or Gemini 3 Flash, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token Limit:&lt;/strong&gt; 1M+ tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;use-pro-agents&quot;&gt;Use Pro Agents&lt;/h4&gt;
&lt;p&gt;Now you can invoke high-level agents directly on your WSL files:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;agy chat &quot;Analyze the structure of this repo and suggest refactoring opportunities&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because you're authenticated as Pro, the agent will use the full index to understand your entire codebase structure.&lt;/p&gt;
&lt;h3 id=&quot;troubleshooting-quick-reference&quot;&gt;Troubleshooting Quick Reference&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Issue &lt;/th&gt;&lt;th&gt;Symptom &lt;/th&gt;&lt;th&gt;Fix&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Network &lt;/td&gt;&lt;td&gt;Browser opens, IDE stays loading &lt;/td&gt;&lt;td&gt;Enable &lt;code&gt;networkingMode=mirrored&lt;/code&gt; in &lt;code&gt;.wslconfig&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Clock Drift &lt;/td&gt;&lt;td&gt;&quot;Invalid Token&quot; or &quot;Login Expired&quot; &lt;/td&gt;&lt;td&gt;Run &lt;code&gt;sudo hwclock -s&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Account Type &lt;/td&gt;&lt;td&gt;&quot;Not Eligible&quot; &lt;/td&gt;&lt;td&gt;Use personal &lt;code&gt;@gmail.com&lt;/code&gt; account&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Missing Scripts &lt;/td&gt;&lt;td&gt;IDE won't connect to WSL &lt;/td&gt;&lt;td&gt;Download and extract scripts from VS Code Marketplace&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Wrong Extension ID &lt;/td&gt;&lt;td&gt;Connection fails silently &lt;/td&gt;&lt;td&gt;Update &lt;code&gt;WSL_EXT_ID&lt;/code&gt; in launcher script&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;UNC Path Warnings &lt;/td&gt;&lt;td&gt;Warnings in terminal output &lt;/td&gt;&lt;td&gt;Use &lt;code&gt;cd /mnt/c &amp;amp;&amp;amp;&lt;/code&gt; in wrapper script&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;common-commands&quot;&gt;Common Commands&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Open current directory in Antigravity
agy .

# Check process usage and diagnostic information
agy --status

# Check version
agy --version

# Get help
agy --help
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;pro-tips&quot;&gt;Pro Tips&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistent Sessions:&lt;/strong&gt; Enable &quot;Persistent Agent&quot; in Antigravity UI to keep agents running even if you close the terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Project Indexing:&lt;/strong&gt; Let Antigravity fully index your project before asking complex questions for better results&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token Limits:&lt;/strong&gt; Pro users get 1M+ token context windows, perfect for analyzing large codebases&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Regional Consistency:&lt;/strong&gt; Avoid using VPNs during authentication to prevent region mismatch issues&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Getting Google Antigravity Pro working on WSL2 requires bridging the gap between the Windows and Linux environments. By following these steps—fixing networking, synchronizing clocks, creating proper symlinks, and enabling interoperability—you'll unlock the full power of Google's agentic IDE.&lt;/p&gt;
&lt;p&gt;The effort is worth it: once configured, you'll have access to powerful AI agents that can understand and refactor your entire codebase, all while working seamlessly in your WSL2 development environment.&lt;/p&gt;
&lt;h3 id=&quot;additional-resources&quot;&gt;Additional Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=example&quot;&gt;Google Antigravity Setup Guide (Video)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl&quot;&gt;VS Code Remote - WSL Extension&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.microsoft.com/en-us/windows/wsl/&quot;&gt;WSL2 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Last updated: January 2026 | Tested on Windows 10 + WSL2 Ubuntu 24.04.3 LTS&lt;/em&gt;&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/10207/fixing-google-antigravity-pro-authentication-on-windows-10-wsl2</guid>
<pubDate>Wed, 21 Jan 2026 17:57:05 +0000</pubDate>
</item>
<item>
<title>Effortless Web App Deployments with GitHub Actions</title>
<link>https://coderlegion.com/8448/effortless-web-app-deployments-with-github-actions</link>
<description>&lt;h4 id=&quot;automating-production-deployments-on-digitalocean&quot;&gt;Automating Production Deployments on DigitalOcean&lt;/h4&gt;
&lt;p&gt;Deploying web applications manually gets painful fast — especially once your project grows.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;azan.lk&lt;/strong&gt;, a Laravel + React application running on a hardened DigitalOcean droplet, I moved from manual SSH-based deployments to a &lt;strong&gt;fully automated CI/CD pipeline using GitHub Actions&lt;/strong&gt;.&lt;br&gt;
Every push to &lt;code&gt;main&lt;/code&gt; now builds, deploys, and optimizes the app automatically — safely and consistently.&lt;/p&gt;
&lt;p&gt;This article gives you a &lt;strong&gt;practical, production-ready overview&lt;/strong&gt; of how to automate deployments without overengineering.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-this-covers&quot;&gt;⚙️ What This Covers&lt;/h3&gt;
&lt;p&gt;This guide focuses on &lt;strong&gt;real-world CI/CD for VPS-based deployments&lt;/strong&gt;, not platform-locked solutions.&lt;/p&gt;
&lt;p&gt;You’ll learn how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automate deployments using &lt;strong&gt;GitHub Actions&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Build Laravel &amp;amp; React &lt;strong&gt;outside the server&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Deploy via &lt;strong&gt;SSH securely&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Run database migrations and optimizations remotely&lt;/li&gt;
&lt;li&gt;Keep your DigitalOcean droplet &lt;strong&gt;lean and stable&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Eliminate repetitive SSH workflows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perfect for portfolios, SaaS apps, client projects, or APIs running on a VPS.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;prerequisites-important-&quot;&gt;Prerequisites (Important)&lt;/h3&gt;
&lt;p&gt;Before adding automation, your server must already be production-ready.&lt;/p&gt;
&lt;p&gt;Make sure you’ve completed these first:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server Hardening&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non-root user&lt;/li&gt;
&lt;li&gt;SSH keys&lt;/li&gt;
&lt;li&gt;Firewall &amp;amp; Fail2Ban&lt;br&gt;
&lt;em&gt;Fortify Your DigitalOcean Droplet: A Step-by-Step Security Guide for Ubuntu 24.04&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;HTTPS &amp;amp; SSL&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;Let’s Encrypt&lt;/li&gt;
&lt;li&gt;Auto-renewal&lt;br&gt;
&lt;em&gt;Unlock Free HTTPS with Let’s Encrypt SSL&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Manual Deployment Setup&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Laravel + PHP-FPM&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Node &amp;amp; Vite&lt;br&gt;
&lt;em&gt;Streamlined App Deployment on Ubuntu 24.04&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once those are done, CI/CD becomes the final layer — not a replacement.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-with-traditional-deployments&quot;&gt;The Problem with Traditional Deployments&lt;/h3&gt;
&lt;p&gt;A typical manual deployment looks like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSH into the server&lt;/li&gt;
&lt;li&gt;Pull latest code&lt;/li&gt;
&lt;li&gt;Run Composer&lt;/li&gt;
&lt;li&gt;Run NPM builds&lt;/li&gt;
&lt;li&gt;Fix permissions&lt;/li&gt;
&lt;li&gt;Run migrations&lt;/li&gt;
&lt;li&gt;Hope nothing breaks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On small VPS instances, Composer and NPM can &lt;strong&gt;consume memory, slow the server, or even crash builds&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And doing this repeatedly?&lt;br&gt;
That’s technical debt waiting to happen.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-you-ll-accomplish-with-ci-cd&quot;&gt;What You’ll Accomplish with CI/CD&lt;/h3&gt;
&lt;p&gt;By the end of this setup, you’ll have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic deployments on every push to &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Builds handled by &lt;strong&gt;GitHub&lt;/strong&gt;, not your VPS&lt;/li&gt;
&lt;li&gt;Secure SSH-based file sync&lt;/li&gt;
&lt;li&gt;Zero manual server logins for deploys&lt;/li&gt;
&lt;li&gt;Reliable, repeatable production releases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Push code → GitHub Actions runs → App goes live&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-approach-works&quot;&gt;Why This Approach Works&lt;/h3&gt;
&lt;p&gt;This setup is optimized for developers who:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;VPS hosting&lt;/strong&gt; instead of managed platforms&lt;/li&gt;
&lt;li&gt;Want &lt;strong&gt;full control&lt;/strong&gt; over infrastructure&lt;/li&gt;
&lt;li&gt;Care about &lt;strong&gt;performance and stability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Prefer simple, understandable pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No Kubernetes.&lt;br&gt;
No overcomplicated workflows.&lt;br&gt;
Just clean automation that works.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;read-the-full-step-by-step-guide&quot;&gt;Read the Full Step-by-Step Guide&lt;/h3&gt;
&lt;p&gt;The full article includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complete &lt;code&gt;deploy.yml&lt;/code&gt; workflow&lt;/li&gt;
&lt;li&gt;GitHub Secrets setup&lt;/li&gt;
&lt;li&gt;SSH key best practices&lt;/li&gt;
&lt;li&gt;Common CI/CD pitfalls (and how to avoid them)&lt;/li&gt;
&lt;li&gt;Production-safe Artisan commands&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://insafnilam.hashnode.dev/effortless-web-app-deployments-mastering-github-actions-with-digitalocean&quot;&gt;Effortless Web App Deployments: Mastering GitHub Actions with DigitalOcean&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-final-thoughts&quot;&gt;✨ Final Thoughts&lt;/h3&gt;
&lt;p&gt;CI/CD isn’t about complexity — it’s about &lt;strong&gt;removing friction&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once this pipeline is in place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deployments become boring (that’s good)&lt;/li&gt;
&lt;li&gt;Servers stay fast&lt;/li&gt;
&lt;li&gt;Mistakes drop dramatically&lt;/li&gt;
&lt;li&gt;Your workflow scales with your project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pair this guide with &lt;strong&gt;Streamlined App Deployment on Ubuntu 24.04&lt;/strong&gt;, and your readers get a complete journey:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manual setup → hardened server → automated production deployments&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/8448/effortless-web-app-deployments-with-github-actions</guid>
<pubDate>Sat, 13 Dec 2025 16:33:23 +0000</pubDate>
</item>
<item>
<title>Streamlined Production Deployment on Ubuntu 24.04</title>
<link>https://coderlegion.com/8135/streamlined-production-deployment-on-ubuntu-24-04</link>
<description>&lt;p&gt;Deploying a full-stack application for production doesn’t have to be complicated — especially if you follow a structured setup. For &lt;strong&gt;azan.lk&lt;/strong&gt;, a Laravel + React project backed by MySQL, I built a fully optimized, secure deploymenton &lt;strong&gt;Ubuntu 24.04&lt;/strong&gt;, running on a DigitalOcean Droplet.&lt;/p&gt;
&lt;p&gt;This post is a quick overview of what the major components you’ll handle.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-this-covers-1&quot;&gt;⚙️ What This Covers&lt;/h3&gt;
&lt;p&gt;This deployment walkthrough is designed for modern full-stack apps and covers the full stack you’d expect in real production:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Laravel backend&lt;/strong&gt; (PHP 8.4, FPM, Composer)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React frontend&lt;/strong&gt; (bundled via Vite)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MySQL database&lt;/strong&gt; (hardened &amp;amp; configured)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx production server&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Node.js + NPM via NVM&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git + SSH for secure deployments&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment setup, permissions, and optimizations&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re deploying a portfolio, client project, SaaS, or API backend, this setup gives you a fast, stable, security-conscious structure.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;before-deployment-essential-prerequisites&quot;&gt;Before Deployment: Essential Prerequisites&lt;/h3&gt;
&lt;p&gt;Make sure your droplet is secure and ready for production.&lt;br&gt;
I recommend completing these two guides first:&lt;/p&gt;
&lt;h4 id=&quot;1-server-hardening-must-do-before-anything-&quot;&gt;1. Server Hardening (Must Do Before Anything)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/8084/fortify-your-digitalocean-droplet-a-step-by-step-security-guide-for-ubuntu-24-04&quot;&gt;Fortify Your DigitalOcean Droplet: A Step-by-Step Security Guide for Ubuntu 24.04&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
A complete walkthrough covering non-root users, SSH keys, firewall, Fail2Ban, and more.&lt;/p&gt;
&lt;h4 id=&quot;2-enable-https-required-for-live-apps-&quot;&gt;2. Enable HTTPS (Required for Live Apps)&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/8116/secure-your-digitalocean-droplet-with-free-https-lets-encrypt-certbot&quot;&gt;Fortify Your Droplet: Unlock Free HTTPS with Let’s Encrypt SSL&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Includes DNS setup, Nginx configs, SSL installation, and auto-renewal.&lt;/p&gt;
&lt;p&gt;Once both are done, your droplet is safe to host real traffic — and ready for deployment.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-you-ll-accomplish&quot;&gt;✨ What You’ll Accomplish&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Install PHP 8.4 with all required extensions for production&lt;/li&gt;
&lt;li&gt;Set up Composer safely using the official installer&lt;/li&gt;
&lt;li&gt;Configure MySQL with proper users, permissions, and best practices&lt;/li&gt;
&lt;li&gt;Install Node.js via NVM for frontend builds&lt;/li&gt;
&lt;li&gt;Set up Git SSH keys for secure repository access&lt;/li&gt;
&lt;li&gt;Configure application permissions for Laravel&lt;/li&gt;
&lt;li&gt;Build and optimize your frontend assets for production&lt;/li&gt;
&lt;li&gt;Set up Nginx with proper routing, headers, and FPM integration&lt;/li&gt;
&lt;li&gt;Run database migrations and seeders&lt;/li&gt;
&lt;li&gt;Troubleshoot common deployment issues like PHP errors, permissions, or SSH problems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every step — with commands, configs, and explanations — is included in the full guide on Hashnode.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;read-the-full-detailed-guide&quot;&gt;Read the Full, Detailed Guide&lt;/h3&gt;
&lt;p&gt;The complete command-by-command walkthrough — including explanations, and best practices — is available on my blog.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Read the full article here:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://insafnilam.hashnode.dev/streamlined-app-deployment-launch-your-laravel-react-project-on-ubuntu-2404-with-digitalocean&quot;&gt;Streamlined App Deployment: Launch Your First Web App on Ubuntu 24.04 with DigitalOcean&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The post includes every command, config file, and optimization used in the real-world deployment of &lt;strong&gt;azan.lk&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-guide-matters&quot;&gt;Why This Guide Matters&lt;/h3&gt;
&lt;p&gt;Most deployment tutorials are outdated, incomplete, or skip critical production details.&lt;br&gt;
This guide focuses on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; (SSL, firewall, SSH hardening)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt; (PHP-FPM tuning, Vite builds)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stability&lt;/strong&gt; (MySQL, permissions, proper Nginx configs)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Developer workflow&lt;/strong&gt; (Git SSH, easy updates, reliable builds)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ideal for developers deploying real-world projects into production.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;A reliable deployment is more than just “upload code and run”:&lt;br&gt;
it’s a combination of &lt;strong&gt;security&lt;/strong&gt;, &lt;strong&gt;performance&lt;/strong&gt;, and &lt;strong&gt;correct configuration&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Full-stack Dev</category>
<guid isPermaLink="true">https://coderlegion.com/8135/streamlined-production-deployment-on-ubuntu-24-04</guid>
<pubDate>Wed, 03 Dec 2025 14:21:31 +0000</pubDate>
</item>
<item>
<title>Secure Your DigitalOcean Droplet with Free HTTPS (Let’s Encrypt + Certbot)</title>
<link>https://coderlegion.com/8116/secure-your-digitalocean-droplet-with-free-https-lets-encrypt-certbot</link>
<description>&lt;p&gt;Once your droplet is hardened with a non-root user, SSH hardening, and a firewall, the next upgrade you &lt;em&gt;must&lt;/em&gt; add is &lt;strong&gt;HTTPS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Without HTTPS, your login pages, API requests, and user data travel in plain text — easy for attackers to sniff or intercept. With HTTPS enabled, you get encrypted traffic, improved trust, and that familiar browser padlock that tells users their connection is safe.&lt;/p&gt;
&lt;p&gt;This post is a quick overview of what the HTTPS setup includes.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;start-here-droplet-hardening-guide-before-https-&quot;&gt;Start Here: Droplet Hardening Guide (Before HTTPS)&lt;/h3&gt;
&lt;p&gt;Before diving into HTTPS, make sure you’ve completed the hardening steps covered in my first guide:&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/8084/fortify-your-digitalocean-droplet-a-step-by-step-security-guide-for-ubuntu-24-04&quot;&gt;Fortify Your DigitalOcean Droplet: A Step-by-Step Security Guide for Ubuntu 24.04&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-you-ll-accomplish-1&quot;&gt;✨ What You’ll Accomplish&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Connect your domain to the droplet&lt;/li&gt;
&lt;li&gt;Configure Nginx properly for production&lt;/li&gt;
&lt;li&gt;Install Certbot + Let’s Encrypt&lt;/li&gt;
&lt;li&gt;Enable secure HTTPS with automatic renewal&lt;/li&gt;
&lt;li&gt;Redirect all HTTP traffic to HTTPS&lt;/li&gt;
&lt;li&gt;Validate the setup with a clean, secure padlock indicator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every step — with the exact commands — is included in the full guide.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-https-matters&quot;&gt;Why HTTPS Matters&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Protects login and API endpoints&lt;/li&gt;
&lt;li&gt;Encrypts form data, cookies, and tokens&lt;/li&gt;
&lt;li&gt;Immediately boosts user trust&lt;/li&gt;
&lt;li&gt;Required by OAuth providers, Stripe, Clerk, Firebase, and more&lt;/li&gt;
&lt;li&gt;Works seamlessly with any backend (Laravel, Node.js, Go, Spring Boot, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whether you're deploying a portfolio, SaaS app, REST API, or production service, &lt;strong&gt;HTTPS is non-negotiable&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;read-the-full-detailed-guide-1&quot;&gt;Read the Full, Detailed Guide&lt;/h3&gt;
&lt;p&gt;The complete command-by-command walkthrough — including explanations, and best practices — is available on my blog.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Read the full article here:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://insafnilam.hashnode.dev/fortify-your-droplet-unlock-free-https-with-lets-encrypt-ssl&quot;&gt;Fortify Your Droplet: Unlock Free HTTPS with Let’s Encrypt SSL&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(Every command and configuration is included — nothing hidden.)&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;after-you-follow-the-guide-your-droplet-will-be-&quot;&gt;After You Follow the Guide, Your Droplet Will Be:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ Fully encrypted with HTTPS&lt;/li&gt;
&lt;li&gt;✅ Protected from MITM attacks&lt;/li&gt;
&lt;li&gt;✅ Auto-renewing SSL forever&lt;/li&gt;
&lt;li&gt;✅ Ready for real production workloads&lt;/li&gt;
&lt;li&gt;✅ Safe for sessions, passwords, and API traffic&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>App Security</category>
<guid isPermaLink="true">https://coderlegion.com/8116/secure-your-digitalocean-droplet-with-free-https-lets-encrypt-certbot</guid>
<pubDate>Wed, 03 Dec 2025 07:59:56 +0000</pubDate>
</item>
<item>
<title>Fortify Your DigitalOcean Droplet: A Step-by-Step Security Guide for Ubuntu 24.04</title>
<link>https://coderlegion.com/8084/fortify-your-digitalocean-droplet-a-step-by-step-security-guide-for-ubuntu-24-04</link>
<description>&lt;p&gt;Launching a production app isn’t just about clean code or beautiful UI — it’s about making sure the server behind it is hardened, protected, and ready for real-world traffic. A poorly configured droplet can be compromised in minutes, and the cost of recovery is far greater than the cost of prevention.&lt;/p&gt;
&lt;p&gt;To help fellow developers avoid those risks, I documented the exact hardening process I used for &lt;strong&gt;azan.lk&lt;/strong&gt;, a Sri Lanka–focused Islamic portal. These steps apply to any project — personal, client-based, or fully commercial.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-you-ll-achieve&quot;&gt;What You’ll Achieve&lt;/h3&gt;
&lt;p&gt;By the end of this guide, you’ll have a droplet that’s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hardened and production-ready&lt;/li&gt;
&lt;li&gt;Protected with SSH key–only access&lt;/li&gt;
&lt;li&gt;Locked down with root login disabled&lt;/li&gt;
&lt;li&gt;Secured with a properly configured UFW firewall&lt;/li&gt;
&lt;li&gt;Reinforced with optional tools like Fail2Ban and unattended security updates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are foundational steps I recommend before deploying &lt;strong&gt;any&lt;/strong&gt; backend, frontend, Docker service, or microservice.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-this-guide-covers&quot;&gt;What This Guide Covers&lt;/h3&gt;
&lt;h4 id=&quot;1-organizing-your-digitalocean-project&quot;&gt;&lt;strong&gt;1. Organizing Your DigitalOcean Project&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Set up a clean DO workspace so your droplets, DNS records, metrics, and backups stay organized.&lt;/p&gt;
&lt;h4 id=&quot;2-building-a-secure-droplet&quot;&gt;&lt;strong&gt;2. Building a Secure Droplet&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Recommended specs for most apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ubuntu 24.04 LTS&lt;/li&gt;
&lt;li&gt;2GB RAM / 2 CPU&lt;/li&gt;
&lt;li&gt;60GB SSD&lt;/li&gt;
&lt;li&gt;Singapore or Bangalore for low latency to Sri Lanka&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-ssh-amp-user-configuration&quot;&gt;&lt;strong&gt;3. SSH &amp;amp; User Configuration&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Create a non-root sudo user&lt;/li&gt;
&lt;li&gt;Add SSH key authentication&lt;/li&gt;
&lt;li&gt;Disable password login&lt;/li&gt;
&lt;li&gt;Disable root login for safety&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-hardening-essentials&quot;&gt;&lt;strong&gt;4. Hardening Essentials&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Strengthen your droplet with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strict SSH configuration&lt;/li&gt;
&lt;li&gt;UFW firewall&lt;/li&gt;
&lt;li&gt;Fail2Ban jail protection&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;5-developer-quality-of-life-enhancements&quot;&gt;&lt;strong&gt;5. Developer Quality-of-Life Enhancements&lt;/strong&gt;&lt;/h4&gt;
&lt;p&gt;Set up a local SSH config so logging into your droplet becomes instant and password-free.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-i-wrote-this&quot;&gt;✨ Why I Wrote This&lt;/h3&gt;
&lt;p&gt;When I first started deploying production systems, server security felt like something for “later.”&lt;br&gt;
Then I learned how vulnerable a fresh droplet is out of the box — and how quickly it can be scanned, probed, or exploited.&lt;/p&gt;
&lt;p&gt;Now I document my setup processes so other developers can deploy with confidence and avoid the pitfalls I learned the hard way.&lt;/p&gt;
&lt;p&gt;This article is perfect if you’re deploying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Laravel, Node.js, or Go applications&lt;/li&gt;
&lt;li&gt;React, Vue, or Next.js frontends&lt;/li&gt;
&lt;li&gt;Docker containers / microservices&lt;/li&gt;
&lt;li&gt;APIs or production websites&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;read-the-full-detailed-guide-2&quot;&gt;Read the Full, Detailed Guide&lt;/h3&gt;
&lt;p&gt;The complete command-by-command walkthrough — including explanations, and best practices — is available on my blog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Read the full article here:&lt;/strong&gt;&lt;br&gt;
 &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://insafnilam.hashnode.dev/fortify-your-digitalocean-droplet-a-step-by-step-security-guide-for-ubuntu-2404&quot;&gt;Fortify Your DigitalOcean Droplet: A Step-by-Step Security Guide for Ubuntu 24.04&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;(Every command and configuration is included — nothing hidden.)&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Security isn’t a one-time task — it’s an ongoing practice.&lt;br&gt;
But this guide gives you a &lt;strong&gt;solid, battle-tested foundation&lt;/strong&gt; for any production environment, whether you’re hosting a small personal app or a full-scale platform.&lt;/p&gt;
</description>
<category>App Security</category>
<guid isPermaLink="true">https://coderlegion.com/8084/fortify-your-digitalocean-droplet-a-step-by-step-security-guide-for-ubuntu-24-04</guid>
<pubDate>Tue, 02 Dec 2025 18:54:08 +0000</pubDate>
</item>
</channel>
</rss>