by Joche Ojeda | Apr 20, 2025 | WSL
My Docker Adventure in Athens
Hello fellow tech enthusiasts!
I’m currently in Athens, Greece, enjoying a lovely Easter Sunday, when I decided to tackle a little tech project – getting Docker running on my Microsoft Surface with an ARM64 CPU. If you’ve ever tried to do this, you might know it’s not as straightforward as it sounds!
After some research, I discovered something important: there’s a difference between Docker Enterprise and Docker Community Edition (CE). While the enterprise version doesn’t support ARM64 yet, Docker CE does have versions for both ARM64 and x64 architectures. Perfect!
The WSL2 Solution
I initially tried to install Docker directly on Windows, but quickly ran into roadblocks. That’s when I decided to try the Windows Subsystem for Linux (WSL2) route instead. Spoiler alert: it worked like a charm!
While you won’t get the nice Docker Desktop UI that Windows users might be accustomed to, the command line interface through WSL2 works perfectly fine. After all, Docker was born on Linux, so running it in a Linux environment makes sense!
Step-by-Step Guide to Installing Docker CE on WSL2
Here’s how I got Docker CE up and running on my Surface using WSL2:
Step 1: Update Your Packages
First, make sure your WSL2 system is up to date:
sudo apt update && sudo apt upgrade -y
Step 2: Install Required Packages
Install the necessary packages to use HTTPS repositories:
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release
Step 3: Add Docker’s Official GPG Key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
Step 4: Set Up the Stable Docker Repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
Step 5: Update APT with the New Repository
sudo apt update
Step 6: Install Docker CE
sudo apt install -y docker-ce docker-ce-cli containerd.io
Step 7: Start the Docker Service
sudo service docker start
Step 8: Add Your User to the Docker Group
This allows you to run Docker without sudo:
sudo usermod -aG docker $USER
Step 9: Apply the Group Changes
Either log out and back in, or run:
newgrp docker
Step 10: Verify Your Installation
docker --version
docker run hello-world
Pro Tip!
If you want Docker to start automatically when you launch WSL2, add the service start command to your .bashrc or .zshrc file:
echo "sudo service docker start" >> ~/.bashrc
Final Thoughts
What started as a potentially frustrating experience turned into a surprisingly smooth process. WSL2 continues to impress me with how well it bridges the Windows and Linux worlds. If you have a Surface or any other ARM64-based Windows device and need to run Docker, I highly recommend the WSL2 approach.
Have you tried running Docker on an ARM device? What was your experience like? Let me know in the comments below!
Happy containerizing! 🐳
by Joche Ojeda | Mar 5, 2025 | C#, dotnet, Uno Platform
Exploring the Uno Platform: Handling Unsafe Code in Multi-Target Applications
This last weekend I wanted to do a technical experiment as I always do when I have some free time. I decided there was something new I needed to try and see if I could write about. The weekend turned out to be a beautiful surprise as I went back to test the Uno platform – a multi-OS, multi-target UI framework that generates mobile applications, desktop applications, web applications, and even Linux applications.
The idea of Uno is a beautiful concept, but for a long time, the tooling wasn’t quite there. I had made it work several times in the past, but after an update or something in Visual Studio, the setup would break and applications would become basically impossible to compile. That seems to no longer be the case!
Last weekend, I set up Uno on two different computers: my new Surface laptop with an ARM type of processor (which can sometimes be tricky for some tools) and my old MSI with an x64 type of processor. I was thrilled that the setup was effortless on both machines.
After the successful setup, I decided to download the entire Uno demo repository and start trying out the demos. However, for some reason, they didn’t compile. I eventually realized there was a problem with generated code during compilation time that turned out to be unsafe code. Here are my findings about how to handle the unsafe code that is generated.
AllowUnsafeBlocks Setting in Project File
I discovered that this setting was commented out in the Navigation.csproj file:
<!--<AllowUnsafeBlocks>true</AllowUnsafeBlocks>-->
When uncommented, this setting allows the use of unsafe code blocks in your .NET 8 Uno Platform project. To enable unsafe code, you need to remove the comment markers from this line in your project file.
Why It’s Needed
The <AllowUnsafeBlocks>true</AllowUnsafeBlocks> setting is required whenever you want to use “unsafe” code in C#. By default, C# is designed to be memory-safe, preventing direct memory manipulation that could lead to memory corruption, buffer overflows, or security vulnerabilities. When you add this setting to your project file, you’re explicitly telling the compiler to allow portions of code marked with the unsafe keyword.
Unsafe code lets you work with pointers and perform direct memory operations, which can be useful for:
- Performance-critical operations
- Interoperability with native code
- Direct memory manipulation
What Makes Code “Unsafe”
Code is considered “unsafe” when it bypasses .NET’s memory safety guarantees. Specifically, unsafe code includes:
- Pointer operations: Using the * and -> operators with memory addresses
- Fixed statements: Pinning managed objects in memory so their addresses don’t change during garbage collection
- Sizeof operator: Getting the size of a type in bytes
- Stackalloc keyword: Allocating memory on the stack instead of the heap
Example of Unsafe Code
Here’s an example of unsafe code that might be generated:
unsafe
{
int[] numbers = new int[] { 10, 20, 30, 40, 50 };
// UNSAFE: Pinning an array in memory and getting direct pointer
fixed (int* pNumbers = numbers)
{
// UNSAFE: Pointer declaration and manipulation
int* p = pNumbers;
// UNSAFE: Dereferencing pointers to modify memory directly
*p = *p + 5;
*(p + 1) = *(p + 1) + 5;
}
}
Why Use Unsafe Code?
There are several legitimate reasons to use unsafe code:
- Performance optimization: For extremely performance-critical sections where you need to eliminate overhead from bounds checking or other safety features.
- Interoperability: When interfacing with native libraries or system APIs that require pointers.
- Low-level operations: For systems programming tasks that require direct memory manipulation, like implementing custom memory managers.
- Hardware access: When working directly with device drivers or memory-mapped hardware.
- Algorithms requiring pointer arithmetic: Some specialized algorithms are most efficiently implemented using pointer operations.
Risks and Considerations
Using unsafe code comes with significant responsibilities:
- You bypass the runtime’s safety checks, so errors can cause application crashes or security vulnerabilities
- Memory leaks are possible if you allocate unmanaged memory and don’t free it properly
- Your code becomes less portable across different .NET implementations
- Debugging unsafe code is more challenging
In general, you should only use unsafe code when absolutely necessary and isolate it in small, well-tested sections of your application.
In conclusion, I’m happy to see that the Uno platform has matured significantly. While there are still some challenges like handling unsafe generated code, the setup process has become much more reliable. If you’re looking to develop truly cross-platform applications with a single codebase, Uno is worth exploring – just remember to uncomment that AllowUnsafeBlocks setting if you run into compilation issues!