How Microsoft and Apple Pushed Me Into Using Linux
Setting up open source software is worth the effort
This post is about what it took for me to figure out the best way of working from my phone, and how obstacles created by Microsoft and Apple influenced where I ended up.
I’ve had plenty of gripes with big tech companies over the many years I’ve used their products. I’m well into the process of extricating myself from their systems today. They aren't making it easy, but I've already found plenty of alternatives that work well for me. Over the past few years, I switched from using Twitter (it was still Twitter when I deleted my account) to Mastodon, I started transitioning my primary email away from Gmail, I switched my primary browser from Chrome to Firefox, and I switched my default search engine to DuckDuckGo. Thanks to Nebula, some of my favourite YouTube content is available outside of Google's ecosystem, but network effect still forces me to use the platform. I've been thinking about trying Ubuntu as an operating system for some time, but my primary use for a desktop computer historically has been gaming. Linux gaming has progressed a lot in large part thanks to SteamOS, but I still begrudgingly want to have access to a Windows installation. Apple hasn't yet managed to push me away from iOS, but I'm sure it's only a matter of time, so I'm thinking about making the jump myself first.
After some struggle, I no longer really need my PC for working on software projects. I can do almost everything from my iPhone. (Not being able to do one important thing seems to be entirely on Apple and I'll address it specifically later in this post.) I hope it will be interesting to detail the things I tried to get to this point that didn't work, and the things I had to learn along the way.
Giving Windows one last chance
At the time I published my first post here, I had only worked on this project using VS Code on Windows. Since then, circumstances have necessitated me working from bed most of the time. I don't mind working on my Windows laptop in bed, but being a chunky old gaming laptop makes it less than ideal for doing so for long periods of time. A more ideal device for coding in bed is outside my current means, so my best option was using my iPhone. I didn't relish the thought of transitioning from two moderately sized monitors to a screen that fits it my hand, but I was determined to make it work as well as it could.
The first iOS app I tried was iSH. It's totally free, and it emulates Alpine Linux with a file system that's accessible through the native Files app. It's limited by being on the App Store, but I think its developers have done admirably within those limitations. Apple's Files not showing hidden files was a significant one. See the scripts below for how I got around it.
The Bash scripts I wrote to deal with hidden files in iSH
make_hidden.sh:
I wrote this first because I was working on files called bashrc and bash_profile, and I needed versions that started with . so Bash would recognize them.
# takes an arbitrary list of file name arguments and creates hidden versions of them
for arg in "$@"
do
if [ -f $arg ]; then
if [ -f .$arg ]; then
read -p ".$arg already exists. Overwrite? (Y/n): " response
if [[ $response =~ [Yy] ]]; then
rm .$arg
cp $arg .$arg
echo ".$arg overwritten"
else
echo skipping $arg
fi
unset response
else
cp $arg .$arg
fi
else
echo "$arg not found"
fi
done
unset arg
unhide.sh:
Later I needed this similar script so I could "unhide" copies of hidden files from my server for editing.
# takes an arbitrary list of hidden file name arguments and creates non-hidden versions of them
for arg in "$@"
do
if [ -f $arg ]; then
unhidden=${arg##*"."}
if [ -f $unhidden ]; then
read -p "$unhidden already exists. Overwrite? (Y/n): " response
if [[ $response =~ [Yy] ]]; then
rm $unhidden
cp $arg $unhidden
echo "$unhidden overwritten"
else
echo skipping $arg
fi
unset response
else
cp $arg $unhidden
unset unhidden
fi
else
echo "$arg not found"
fi
done
unset arg
Thanks to my latest (hopefully final) setup I don't need these anymore.
My recently refreshed Git knowledge gave me an idea: I didn't want to store every single file necessary for my projects on my phone, so perhaps I could figure out how to use Git to work with only a subset of a repository. I was stymied by outdated information on workarounds for that use case, then I found the simpler, modern solution after thoroughly reading a thread on <well-known QA service>. git-sparse-checkout is still marked as experimental, so I won't claim it's the correct way to do things, just that it worked exactly as expected for the time I used it.
With git-sparse-checkout and iSH, I was able to download only the website content to my phone, modify it, and push it wherever I needed it. Though if all I needed to do was compose and upload posts, I would have found a much easier way to do it. I still needed my Windows machine to actually publish the site, as well as–critically–run the development server so I could test new pages in a browser. I briefly considered trying to automate things using Git hooks, but it seemed like too much work, so I decided I needed to get more software involved.
A component of one of my university courses was learning how to work with a Raspberry Pi Zero W over WiFi with OpenSSH. So I had a little experience with SSH, and I was aware of how ubiquitous it was. That course had only covered using SSH clients outside of Linux, so I needed a little help in the form of this tutorial by Scott Hanselman to set up the Windows OpenSSH server. After working through some additional Windows issues not covered by the tutorial, I had sshd running on my PC. Through iSH on my phone, I was then able to manage the Eleventy development server and publish the site. Manually using Git to sync files was still a major annoyance, but I got too sidetracked to consider Git hooks again.
Next, I took a significant detour trying to "improve" remote development on my PC. I dabbled with some Bash scripting, and I tried to make Windows run without a GUI, all the time feeling I had to wrestle Windows into doing what I wanted–if it let me do what I wanted at all. A couple of my "favourite" problems were having to remove /dev/null in a Bash script because Git Bash couldn't use the Windows equivalent, NUL, and having to resort to adding the location of bash.exe specifically to Windows' PATH environment variable to get Windows to find it, despite Windows having no trouble finding git.exe in the same folder. Eventually, though I hadn't exhausted my options, I had exhausted my patience for Microsoft's nonsense. I no longer cared if it was going to take more work to set up a Linux server to do the same things. I was finally ready to put the work in to embrace open source software.
The home Ubuntu server
If I were to make a recommendation to a friend who was also sick of Microsoft and Apple operating systems, I'd tell them to grab the latest LTS version of Ubuntu Desktop. I decided to make things a little more difficult for myself. When I was first considering installing Linux on my old PC, I thought I wanted to self-host a website. I had removed the graphics card and all the storage save the now pitifully small 128 gigabyte SSD, so installing an entire GUI seemed like a waste of resources. I installed Ubuntu Server and tinkered with it for a few days. I didn't get very far before I lost interest. (This was before I had ever used SSH, so constantly having to move between the server and my new PC didn't make for a great experience.) When Windows made me resolve to spend my effort on an operating system I could actually control, my old PC still had that older version of Ubuntu Server installed. I had a much better idea of what I wanted to do, and Ubuntu Server still looked like the operating system for the job.
The first hurdle I encountered was the original Ubuntu Server installation. When I tried to update to the latest version of the operating system, the update failed twice. On top of that, I had elected to fully encrypt the storage drive. I hadn't lost the encryption password, but I convinced myself that I would have to physically access the computer to enter the encryption password every time it restarted. (I now think there could be some way to decrypt a fully encrypted drive remotely, but finding out how hasn't been a priority.) Some cursory research indicated that removing the encryption was effectively impossible, so I decided I might as well pave the whole thing and start over. I hadn't left anything I cared about on that drive anyway.
Getting a fresh Ubuntu Server installation on that machine proved more complicated than I expected. I don't know if it was the encrypted drive, the older version of Balena Etcher I used, the wonky existing Ubuntu installation, or something else entirely, but the first fresh installation failed so spectacularly that it left the operating system inoperable and made a complete mess of the USB drive I used. Never before had I plugged in a USB drive that Windows couldn't even recognize well enough to demand it be formatted. Fortunately, Windows' Disk Management could still see it, so I hadn't managed to completely destroy it. To be safe, I gave it a full format and flashed it with the new Ubuntu Server image using the latest version of Rufus. The second installation worked, but I must have managed to type the same wrong character(s) twice while setting the administrator password, because I got locked out of the system without ever having logged in. I was too frustrated to consider whether waiting for the lock to expire would be faster than another full installation, so I did the thing I could do immediately and started installing Ubuntu for the third time. At least I didn't have to flash the USB drive again. That time I had no trouble logging in, so I started configuring things.
I wanted to get to the point where I could work on the server from my phone as soon as possible. Sitting on my couch hunched over a small old VGA monitor supported by a convenient folding chair was even worse than working at my desk. Unlike the first time I configured Ubuntu Server, I knew exactly what I needed to do. I updated the pre-installed software packages and assigned the server a static IP address using my router, since I didn't want to learn how to set a static IP on the command line just then. I configured the firewall to deny all incoming traffic except from my local network using ufw, and set up the SSH server so I could connect with iSH. Copying the public SSH encryption key from iSH to the server using OpenSSH's provided script worked the first time, unlike with Windows. Once I rebooted and tested the SSH connection one more time, I was confident I could do everything else from my phone.
Getting iOS and Ubuntu in unison
In less time than I would have anticipated a year prior, I was at the same place with that Ubuntu server I had gotten to with my PC. I could easily manage the Eleventy development server, upload and download files, and publish to my Codeberg Pages repository from anywhere in my WiFi network's range. I could have tolerated that situation, but manually syncing files between devices was still getting in the way. I wasn't sure what I needed specifically, so I did some searching for how to handle remote software development on an iPhone. I eventually found some useful tools to test, but I had to get pretty specific with my queries to not get pages of results about remote iOS developer jobs.
I gave Working Copy a try, but quickly determined it wasn't the right tool. It seemed great at streamlining working with Git on iOS, but it didn't seem to do anything I couldn't do using iSH with a little more effort. I came across two iOS code editor apps in the same listicle that sounded promising: Buffer and Textastic. They were both one time purchases with no additional monetization (which is always a good sign to me), and they happened to be the same price. Textastic was the #1 app in the developer tools category, with better reviews, better documentation, and more customization. Buffer, however, had a pretty slick website, and it listed a killer feature there that I couldn't find anywhere in Textastic's excellent manual: "Edit files directly on your server without download/upload." If Buffer could do that while being a reasonably useable code editor, nothing Textastic could offer could compare. That did not turn out to be the case. I'm not writing this post using Buffer.
I paid my $12.99 plus tax for Buffer, and after quick look at the settings menu, got straight to the point. Buffer instantly failed to connect to my server, and offered an unhelpfully vague error message. I double-checked the SSH settings and tested them again in iSH, and they worked perfectly there. I gave the connection a few more tries in Buffer, but kept getting the same vague error. Then I decided that if I was going to have to put in a lot more effort to get the app to do the thing I bought it for, I should find out how it was as an editor first. I think that decision ended up saving me some frustration, if not some time. Over the course of an hour trying to work on local files, Buffer froze and/or crashed at least five times. Immediately after the app recovered from one last crash, an iOS standard "If you're enjoying <app>, please rate it" pop up appeared. I normally dismiss those without much thought, but this time I had plenty to say about the app. (I do try to remember to write reviews for apps I really like, but I much prefer to do it on my own terms.) So I gave Buffer a one star rating, left a review explaining why, uninstalled it, and requested a refund–which I later received. A couple search queries later, I actually managed to find an article that discussed remote software development on an iPhone. I'm kicking myself for not bookmarking it at the time, because I can't find it again and I'd really like to credit the author. It goes over a few options, but I didn't spend much time thinking about most of them, because the first one sounded great. code-server: remote-hostable VS Code with a browser interface.
Despite my growing animosity toward Microsoft, I'm still reasonably happy with VS Code. It happened to be the IDE used in a tutorial I followed years ago, so I picked it up it without much thought, and kept using it as it handled everything I needed an IDE to do well. So I thought code-server would mean using the tool I was most comfortable with from my phone, as well as from any other device with a web browser. I'm sad to also report that I am not writing this post in a web browser. I can't recall much about what it took to get code-server to even render a web page. I probably had to forward a port or two, and I know I even tried building it from source code before giving up. The best I got out of code-server with my setup was even worse than Buffer. I actually edited and saved a file or two using Buffer. code-server's web interface was so unresponsive for me that I never even got a file open. Unlike with Buffer, I think the failing is more likely in myself or my setup than the software. I think either I didn't find the right way to configure things for my situation, or something about my hardware makes running a useable code-server instance impossible (the iPhone part if I had to guess).
So I searched yet again. I found this article by Willem Middelkoop, which would (eventually) lead me to the solution I'm using today. It describes using the cross-platform software Unison to synchronize files from a tablet to a remote server, then using the app Secure Shellfish to integrate that remote server into the native Files app on an iPhone. Unison being free and open source made it the obvious thing for me to try first. I thought I wouldn't even need another iOS app if I could just keep the files I was working on synchronized between iSH and my server.
Then I ran into a major limitation of iSH for the first time: its emulation is impressive, but it isn't capable of everything standard Alpine Linux can do. On top of that, the software package repositories available through iSH have to be custom ones approved by Apple. (I since found a cool workaround that lets you use the official Alpine repositories on the iSH wiki, but that only removes the Apple-imposed limitation.) So the software you can run in iSH is mostly limited to older versions. Unison was available, so I tried getting the default versions of Unison on Ubuntu and iSH to talk to each other. I had no luck with that straightforward approach. I noticed the Unison versions reported by the two systems were different, so even though I didn't yet know backwards compatibility was a well understood problem with older versions of Unison, it occurred to me to try matching the server version with the iSH version. I must have missed something easy and/or obvious, because I'm still not clear on the correct way to get an older version of a package, either from the default Ubuntu repository or from GitHub. What came closest to working was downloading the source code for the right Unison version and building it right on the server. (I think it's likely I made the mistake of using the wrong compiler version. My later reading told me that even two instances of the same older version of Unison compiled with two different compiler versions wouldn't work together. With the difficulty I had getting an old version of Unison, I suspect I couldn't be bothered to do the same thing for the compiler and just used whatever version Ubuntu gave me.) The freshly compiled Unison made it through the tutorial in Unison's manual without a hitch, and showed no signs of trouble as I configured it. Unfortunately when I tried to start syncing files I actually wanted to work on, I ran into error after error and eventually couldn't think of anything else to try.
Then I was back to DuckDuckGo, but with a more specific idea of what I wanted. Sure I couldn't get Unison working, but there had to be other file synchronization software out there, and maybe that would work in iSH. Syncthing sounded better than Unison, and I even found a version of it I could install in iSH. The fact that I found it in the Alpine repositories and not the iSH ones was a warning sign I ignored. The initial run of Syncthing caused a kernel panic for iSH, something I hadn't seen it do before that point. So I have to admit Apple is a little right in restricting iSH's available packages. Some guardrails are warranted, but I think a warning prompt before installing known unstable or untested packages would be enough. Having done what felt like the most I could do with the free tools available, it was time to just pay somebody to solve the problem for me.
The right tools
In the process of struggling with Unison, I installed and started using Shellfish. It was a significant terminal upgrade over iSH, and, most importantly, it had the aforementioned feature of integrating a remote server with the Files app. What kept me on the file synchronization path for so long was that full Files integration was a feature only available in the pro version of Shellfish. My time isn't worth much right now, so if I can save any amount of money by spending time, especially spending time on something fun and/or interesting, I'm going to do it. By the time Syncthing was causing a kernel panic in iSH, I was bored and frustrated. I wanted to get to work on my Anytype project (more on that soon, I promise), and all I had to do was cough up some cash.
So I bought the pro version of Shellfish. $50 for the lifetime pro unlock was a lot more than I'm comfortable spending on an iOS app, but the other option was a subscription, and I much prefer to pay for something once and get it over with when I can afford to. Also, I really like Shellfish. The ads in the free version can be disruptive, but they're tame by free app standards and I think I only saw ads for the pro version and the developer's other apps. It has significantly more extra keyboard keys than iSH, including some like delete, home, and end that I really feel the absence of in other apps now. Some other features it has that iSH doesn't include multiplexing support, password integration, and terminal persistence support. It even has a command snippets feature that I completely forgot about until going back into the settings looking for other things to praise while writing this. All of that had been very easy to use. Shellfish's development seems to be mostly a solo effort, so it even feels good to support it. With my server near seamlessly integrated with the Files app by Shellfish, I could treat files on my server just as though they were stored on my phone, without the hassle of manually copying them back and forth.
So now I can address the thing I've teased a few times: what I'm actually using to write this post. For my first two posts, I did the bulk of the composition in Anytype. I really like being able to put all the information I work with in one place. At least in theory, Anytype is that one place. Unfortunately, Anytype is in beta, so I had to pay an early adopter tax in the form of time spent working around underdeveloped features. Also, Anytype's mobile app is behind the desktop version in terms of features, so that further limits what Anytype can do on my phone. For my first post, I did most of the writing in Anytype and pasted the text into VS Code on my desktop to add markdown formatting. That made me comfortable enough with markdown to add formatting characters as I wrote, so I tried that when composing my second post. I quickly discovered that Anytype automatically processes markdown formatting characters as you input them and adds the respective formatting while removing the characters. That sounds helpful for somebody who's working on a more finished product, but it's the opposite for me trying to write actual markdown. Beta being beta, I couldn't turn that feature off, so I found a workaround: I used alternate characters that were available on the iPhone keyboard, that I was sure would never naturally appear in my writing, in place of the proper markdown formatting characters. Then I wrote a basic Python script to find and replace those characters in case I needed to do the same for more posts later.
My quick and dirty Python script
I put a little extra work in to encapsulate the character replacements so I could easily modify them later, but I doubt I'm coming back to this.
"""
A script for cleaning up text copy-pasted from Anytype
"""
def add_backticks(atstr: str) -> str:
return atstr.replace("•", "`")
def add_underscores(atstr: str) -> str:
return atstr.replace("¥", "_")
if __name__ == "__main__":
out_str = ""
with open("sample_copy.txt", "r", encoding = "utf-8") as f:
for line in f:
line = add_backticks(line)
line = add_underscores(line)
out_str += line
with open("atclean.txt", "w", encoding = "utf-8") as f:
f.write(out_str)
The other text editor I used to use was Koder, since coding in Anytype is completely out of the question right now. It was the best-sounding free code editing app I found, even boasting remote connectivity features. I used it to test every option I found for working from my phone, and as of a few days ago, I thought it would continue to serve as my code editor. There had been plenty of small issues I had dismissed though. It wasn't nearly as bad as Buffer was for me, and it was free, so I think that made me cut it some slack. There were a few things I tried that it seemed incapable of doing, and it would crash or freeze instead of telling me no.
The first crash I remember happened when I was trying to sync Koder with a remote folder. It had no trouble connecting to my server and uploading and downloading, so when I found its "sync with ftp" option, I thought that was it. But it crashed every time I tried to sync, no matter the configuration. I had only thought of it as an editor, so I wasn't very disappointed. Some other things caused instability, but they never got in the way of editing. Then I realized that there was no way to manage the list of recent files Koder maintains, so that feature would become useless once that list was long enough. Those little annoyances kept piling up, but I could still edit code much more easily than in Apple's Notes or Anytype, so I didn't make a change. Finally, with remote development on my phone underway at last, I ran into something I needed a text editor to do that Koder couldn't do: I needed to edit a Nunjucks template file. I found out that Koder just refuses to open files with extensions it doesn't recognize, and even certain files with no extension at all, inexplicably.
So at last I'll reveal that I'm writing this post using Textastic. I started with Anytype out of habit, but the first time I tried to add markdown formatting characters, the familiar frustration reminded me that I had bought Textastic a few hours earlier to edit that Nunjucks template. Textastic sounded great when I was comparing it to Buffer, and I had solved being able to edit remote files. So far, using Textastic has been a breeze. I just added a couple of directories from my server as external folders, and everything has worked perfectly. It can do its own SFTP connections, but Shellfish makes that unnecessary for the most part. It's no substitute for multiple monitors, a mouse, and a full keyboard, but for working on an iPhone, I've found next to nothing to complain about. It seems to be #1 in developer tools for a reason, so sometimes you do get what you pay for. However, I don't regret the time I spent trying to find free alternatives. Sure I'd have gotten back down to business faster if I'd spent the money right away, but I'd have learned a lot less and I'd have significantly less to say.
Going forward with open source
For the time being, I don't need my desktop PC for anything. I still have to use my Windows laptop when I need Firefox's developer tools, since I haven't found a way to access something similar on my iPhone without connecting it to the actual Mac I don't have. I'm more free from Microsoft and Google than I've ever been since I started using their products, but I still have work to do on the Apple front. I'm glad I managed to make my humble iPhone SE work for me for the time being, but planned obsolescence will come for it eventually. When it does, I'm getting a phone that lets me choose its operating system. I came across Ubuntu Touch the other day, so maybe I'll try that.
I don't think I'm uninstalling Windows any time soon. I'm sure I'll want to play video games on a reasonably sized screen using a nice keyboard and mouse in the future, and I expect Windows will remain my best option for doing so for a while. I am planning to also install Ubuntu Desktop when I'm capable of working at a desk again. And then I want to try working on my various projects using only open source software. It might take more effort to set everything up, but now I think the freedom and control will be worth it.