OS Revisit: Ghostty and Alexandrie

Published: Jan 22, 2026 by Isaac Johnson

I tried out Mitchel’s Ghostty last February and wasn’t impressed then, but at the time it was very new. I see more and more folks using it so I figured it might be a good time to revisit.

Additionally, I mentioned Alexandrie a few weeks back in a blog post. While one feature of it is being a Markdown editor, there looked to be a lot more going on and I wanted to really test it.

Ghostty

We can follow the instructions to install with a one line command

/bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)”

I couldn’t get it to work as a non-root user, but as root it installed

builder@LuiGi:~/Workspaces$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)"

Installing/Updating Ghostty...
Downloading ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 15.8M  100 15.8M    0     0  2826k      0  0:00:05  0:00:05 --:--:-- 3399k
Installing ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
[sudo: authenticate] Password: 
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'ghostty' instead of './ghostty_1.2.3-0.ppa1_amd64_25.10.deb'
Solving dependencies... Done
The following additional packages will be installed:
  libgtk4-layer-shell0
The following NEW packages will be installed:
  ghostty libgtk4-layer-shell0
0 upgraded, 2 newly installed, 0 to remove and 18 not upgraded.
Need to get 17.4 kB/16.6 MB of archives.
After this operation, 66.6 kB of additional disk space will be used.
Get:1 /home/builder/Workspaces/ghostty_1.2.3-0.ppa1_amd64_25.10.deb ghostty amd64 1.2.3-0~ppa1 [16.6 MB]
Get:2 http://us.archive.ubuntu.com/ubuntu questing/universe amd64 libgtk4-layer-shell0 amd64 1.0.4-2 [17.4 kB]
Fetched 17.4 kB in 0s (45.6 kB/s)                    
Selecting previously unselected package libgtk4-layer-shell0:amd64.
(Reading database ... 341368 files and directories currently installed.)
Preparing to unpack .../libgtk4-layer-shell0_1.0.4-2_amd64.deb ...
Unpacking libgtk4-layer-shell0:amd64 (1.0.4-2) ...
Selecting previously unselected package ghostty.
Preparing to unpack .../ghostty_1.2.3-0.ppa1_amd64_25.10.deb ...
Adding 'diversion of /usr/share/terminfo/g/ghostty to /usr/share/terminfo/g/ghostty.distrib by ghostty'
Unpacking ghostty (1.2.3-0~ppa1) ...
Setting up libgtk4-layer-shell0:amd64 (1.0.4-2) ...
Setting up ghostty (1.2.3-0~ppa1) ...
update-alternatives: using /usr/bin/ghostty to provide /usr/bin/x-terminal-emulator (x-terminal-emulator) in auto mode
Processing triggers for desktop-file-utils (0.28-1) ...
Processing triggers for hicolor-icon-theme (0.18-2) ...
Processing triggers for gnome-menus (3.36.0-3ubuntu2) ...
Processing triggers for libc-bin (2.42-0ubuntu3) ...
Processing triggers for man-db (2.13.1-1) ...
N: Download is performed unsandboxed as root as file '/home/builder/Workspaces/ghostty_1.2.3-0.ppa1_amd64_25.10.deb' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
builder@LuiGi:~/Workspaces$ sudo su - 
root@LuiGi:~# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/mkasberg/ghostty-ubuntu/HEAD/install.sh)"

Installing/Updating Ghostty...
Downloading ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 15.8M  100 15.8M    0     0  2645k      0  0:00:06  0:00:06 --:--:-- 2965k
Installing ghostty_1.2.3-0.ppa1_amd64_25.10.deb...
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Note, selecting 'ghostty' instead of './ghostty_1.2.3-0.ppa1_amd64_25.10.deb'
ghostty is already the newest version (1.2.3-0~ppa1).
Solving dependencies... Done
0 upgraded, 0 newly installed, 0 to remove and 18 not upgraded.

Launching with ghostty we see a nice font improvement over the standard Ubuntu terminal

/content/images/2026/01/ghostty-01.png

<Old Man> So this last time I tried getting glasses they offered me “progressives”. No, these aren’t Bernie/AOC endorsed glasses, they are the hip term for ‘bifocals’. They suck. OMG, i want to vomit wearing them. the things move in different speeds at the bottom half giving you a “drunk goggles” view. So i refuse to wear them. So now I wear cheaters (what old people call reading glasses) when in bed and otherwise i just hold the phone/laptop out. You don’t really lose your near sight focus till your 40s so if you are younger, enjoy that sh** while you have it. I wish I would have known I needed to periodically use my close up vision or i would lose it when i was in my 20s and 30s. In any case, yes, old man keeps yammering.. in any case, I like this font way better for when I’m using the higher resolution 4k laptop screen… </Old Man>

Since I want to try it a bit now that my main driver is Linux, I pinned it to the dock

/content/images/2026/01/ghostty-02.png

I can use ctrl + for increasing font size and ctrl - for lowering, which seems familiar

/content/images/2026/01/ghostty-03.png

We can use the command ghostty +list-keybinds --default to see key bindings that are set by default (all of them can be changed as well)

/content/images/2026/01/ghostty-04.png

Because Ghostty is a terminal, not a shell, it used BASH and showed me my history which was nice. My biggest gripe with MacOS and WSL nowadays is it keeps switching to zsh and I have to change it back

/content/images/2026/01/ghostty-05.png

The super (windows key) + ctrl + bracket for moving to preview and next tabs are fine, but the default of ctrl + alt + arrow keys to move around split panes comes into conflict with Gnome in Ubuntu which uses ctrl + alt + arrow right/left for switching workspace windows.

Having the split windows means I don’t need tmux to accomplish having several panes going in the same window. For instance, I can have watch free -m and top going to watch memory and cpu then run a command in the third pane

/content/images/2026/01/ghostty-06.png

While the close commands like close window tend to just end the whole Ghostty session, you can easily close terminal panes by just using exit to end that SSH session and the pane goes away

In the actual menu navigation of Ghostty, we can review, run and search for commands so those that like to use their mouse/trackpad can always go that way

/content/images/2026/01/ghostty-07.png

Next, we’ll look again at Alexandrie, but I did want to point out that I used the split view in Ghostty to make it easier to update docker compose with images I had just built and pushed

/content/images/2026/01/alex2-19.png

Alexandrie

When I did that previous blog post on Alexandrie, I just tried it with localhost and docker.

Let’s do TLS and try a proper ingressed version.

I’ll first bring Alexandrie to an alternate docker host

builder@bosgamerz9:~$ git clone https://github.com/Smaug6739/Alexandrie.git
Cloning into 'Alexandrie'...
remote: Enumerating objects: 21479, done.
remote: Counting objects: 100% (557/557), done.
remote: Compressing objects: 100% (216/216), done.
remote: Total 21479 (delta 477), reused 351 (delta 341), pack-reused 20922 (from 4)
Receiving objects: 100% (21479/21479), 67.63 MiB | 30.23 MiB/s, done.
Resolving deltas: 100% (14411/14411), done.
builder@bosgamerz9:~$ cd Alexandrie/
builder@bosgamerz9:~/Alexandrie$ cp .env.example .env
builder@bosgamerz9:~/Alexandrie$ 

I’m going to need at least three A names to work with my Nginx ingress as I cannot really do port routing with my setup (with TLS that is). I could do HTTP with NodePorts and forwarding and take DNS out of the picture, but then I would need to solve certificates manually.

builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "6b6b33fa-a53d-4544-bfa5-9eef20a1778d",
  "fqdn": "alex.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex",
  "name": "alex",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}
builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex-cdn
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "14e30bc3-eff6-4bdd-96a2-e4aee1702d8b",
  "fqdn": "alex-cdn.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex-cdn",
  "name": "alex-cdn",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}
builder@LuiGi:~/Workspaces$ az account set --subscription "Pay-As-You-Go" && az network dns record-set a add-record -g idjdnsrg -z tpk.pw -a 174.53.161.33 -n  alex-api
{
  "ARecords": [
    {
      "ipv4Address": "174.53.161.33"
    }
  ],
  "TTL": 3600,
  "etag": "99b46428-6826-4192-968c-fda15e3ae8f7",
  "fqdn": "alex-api.tpk.pw.",
  "id": "/subscriptions/d955c0ba-13dc-44cf-a29a-8fed74cbb22d/resourceGroups/idjdnsrg/providers/Microsoft.Network/dnszones/tpk.pw/A/alex-api",
  "name": "alex-api",
  "provisioningState": "Succeeded",
  "resourceGroup": "idjdnsrg",
  "targetResource": {},
  "trafficManagementProfile": {},
  "type": "Microsoft.Network/dnszones/A"
}

With these three DNS entries, I can then create a functional manifest to use them:

spoiler alert: for those following along, I’ll later realize i missed the proxy body size for CDN uploads and that the right port for CDN is 9005 not 9000

---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alexint
    port: 8200
    protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-api-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alex-apiint
    port: 8201
    protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-cdn-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alex-cdnint
    port: 9000
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex
    port: 80
    protocol: TCP
    targetPort: 8200
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-api-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex-api
    port: 80
    protocol: TCP
    targetPort: 8201
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-cdn-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex-cdn
    port: 80
    protocol: TCP
    targetPort: 9000
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-external-ip
  generation: 1
  name: alexingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex.tpk.pw
    secretName: alex-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-api-external-ip
  generation: 1
  name: alex-apiingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-api.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-api-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-api.tpk.pw
    secretName: alex-api-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.org/websocket-services: alex-cdn-external-ip
  generation: 1
  name: alex-cdningress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-cdn.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-cdn-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-cdn.tpk.pw
    secretName: alex-cdn-tls

Then apply it

$ kubectl apply -f ./ingress-alex.yaml 
endpoints/alex-external-ip created
endpoints/alex-api-external-ip created
endpoints/alex-cdn-external-ip created
service/alex-external-ip created
service/alex-api-external-ip created
service/alex-cdn-external-ip created
ingress.networking.k8s.io/alexingress created
ingress.networking.k8s.io/alex-apiingress created
ingress.networking.k8s.io/alex-cdningress created

In the .env file, I can now use those URLs

builder@bosgamerz9:~/Alexandrie$ cat .env | head -n21 | tail -n3
FRONTEND_URL=https://alex.tpk.pw
API_URL=https://alex-api.tpk.pw
CDN_URL=https://alex-cdn.tpk.pw

Then I can docker compose up to bring up the service

builder@bosgamerz9:~/Alexandrie$ docker compose up -d
[+] Running 45/45
 ✔ backend Pulled                                                                                                                                                                                        14.3s 
   ✔ fd4aa3667332 Pull complete                                                                                                                                                                           8.4s 
   ✔ bfb59b82a9b6 Pull complete                                                                                                                                                                           8.6s 
   ✔ 017886f7e176 Pull complete                                                                                                                                                                          10.7s 
   ✔ 62de241dac5f Pull complete                                                                                                                                                                          10.7s 
   ✔ 2780920e5dbf Pull complete                                                                                                                                                                          10.7s 
   ✔ 7c12895b777b Pull complete                                                                                                                                                                          10.7s 
   ✔ 3214acf345c0 Pull complete                                                                                                                                                                          10.7s 
   ✔ 52630fc75a18 Pull complete                                                                                                                                                                          10.7s 
   ✔ dd64bf2dd177 Pull complete                                                                                                                                                                          10.7s 
   ✔ 4aa0ea1413d3 Pull complete                                                                                                                                                                          10.7s 
   ✔ dcaa5a89b0cc Pull complete                                                                                                                                                                          10.8s 
   ✔ 069d1e267530 Pull complete                                                                                                                                                                          10.8s 
   ✔ 6191b4b5532e Pull complete                                                                                                                                                                          10.8s 
   ✔ e92289148968 Pull complete                                                                                                                                                                          11.6s 
   ✔ 7db81a621940 Pull complete                                                                                                                                                                          11.6s 
   ✔ 8a3362ac00f3 Pull complete                                                                                                                                                                          11.6s 
 ✔ mysql Pulled                                                                                                                                                                                          13.7s 
   ✔ 16506d4b4233 Pull complete                                                                                                                                                                           5.5s 
   ✔ 3387bdf9bfcc Pull complete                                                                                                                                                                           5.5s 
   ✔ 5a35458f48a1 Pull complete                                                                                                                                                                           5.5s 
   ✔ 1bed572afc9f Pull complete                                                                                                                                                                           5.6s 
   ✔ f3e7871685d1 Pull complete                                                                                                                                                                           5.6s 
   ✔ 9e9c9ba70723 Pull complete                                                                                                                                                                           5.6s 
   ✔ be113d11b355 Pull complete                                                                                                                                                                           6.1s 
   ✔ 05bdba050124 Pull complete                                                                                                                                                                           6.1s 
   ✔ 54a2bfb30cdf Pull complete                                                                                                                                                                          12.1s 
   ✔ 05ed66656b21 Pull complete                                                                                                                                                                          12.1s 
   ✔ 1c0ceff8a81b Pull complete                                                                                                                                                                          12.1s 
 ✔ rustfs Pulled                                                                                                                                                                                         16.3s 
   ✔ 7d773df7a2be Pull complete                                                                                                                                                                           8.7s 
   ✔ 39838f5db0cf Pull complete                                                                                                                                                                           9.0s 
   ✔ 67b398cfa20b Pull complete                                                                                                                                                                          11.3s 
   ✔ ffd60cccecd6 Pull complete                                                                                                                                                                          11.3s 
   ✔ 4f4fb700ef54 Pull complete                                                                                                                                                                          11.4s 
   ✔ 7746ba9768f2 Pull complete                                                                                                                                                                          11.4s 
 ✔ frontend Pulled                                                                                                                                                                                       11.2s 
   ✔ 1074353eec0d Pull complete                                                                                                                                                                           5.2s 
   ✔ d53378de7b14 Pull complete                                                                                                                                                                           8.2s 
   ✔ 1e51518bad62 Pull complete                                                                                                                                                                           8.3s 
   ✔ df42fde70614 Pull complete                                                                                                                                                                           8.3s 
   ✔ 2fee64f62ee0 Pull complete                                                                                                                                                                           8.3s 
   ✔ 4e30f5080197 Pull complete                                                                                                                                                                           8.7s 
   ✔ a4ee522d4181 Pull complete                                                                                                                                                                           8.7s 
   ✔ f806d52c7133 Pull complete                                                                                                                                                                           8.7s 
[+] Running 8/8
 ✔ Network alexandrie_alexandrie-network  Created                                                                                                                                                         0.1s 
 ✔ Volume "alexandrie_mysql_data"         Created                                                                                                                                                         0.0s 
 ✔ Volume "alexandrie_rustfs_data"        Created                                                                                                                                                         0.0s 
 ✔ Volume "alexandrie_rustfs_logs"        Created                                                                                                                                                         0.0s 
 ✔ Container alexandrie-rustfs            Healthy                                                                                                                                                         5.8s 
 ✔ Container alexandrie-mysql             Healthy                                                                                                                                                        10.8s 
 ✔ Container alexandrie-backend           Started                                                                                                                                                        10.9s 
 ✔ Container alexandrie-frontend          Started   

After the certs were clear, I could check the main site

/content/images/2026/01/alex2-01.png

I can then create an account

/content/images/2026/01/alex2-02.png

After account create, I was redirected to login. Once I login, I see the main dashboard

/content/images/2026/01/alex2-03.png

From there I can create a new workspace

/content/images/2026/01/alex2-04.png

I can then write a do a sample article, which by default is private. Flipping a toggle will make it public

/content/images/2026/01/alex2-05.png

Okay, it worked, but that URL looks pretty garbage

/content/images/2026/01/alex2-06.png

I can set some fields

/content/images/2026/01/alex2-07.png

But they didn’t really affect the page other than to render the markdown properly

/content/images/2026/01/alex2-08.png

In the editor there are some nice WYSIWYG controls for doing things like creating tables

/content/images/2026/01/alex2-09.png

Those just insert the proper markdown or HTML. I can still use Preview to see a rendered version

/content/images/2026/01/alex2-10.png

Interestingly, if I move from Public to Private, it doesn’t have a redirect page or a 404, rather shows a gray page (like a paywall might show)

/content/images/2026/01/alex2-11.png

One thing that threw me of a bit was the “Thumbnail”. A URL to an image didn’t work and it took searching the code a bit to figure out that it wants the contents of an SVG file. I use an Adobe icon as a small example which seemed to work:

/content/images/2026/01/alex2-12.png

If you put in SVG for the “Icon” or Emoji it shows in the nav on the left (here i used the AA logo)

/content/images/2026/01/alex2-13.png

And how it looks on the rendered page:

/content/images/2026/01/alex2-14.png

The print view cleans a lot of navigation up which works well for published documents

/content/images/2026/01/alex2-15.png

CDN

Part of the reason I wanted to revisit the app was to see what the “CDN” would provide.

I tried to upload a small 1.5Mb video, but just saw a quickly disappearing error

/content/images/2026/01/alex2-16.png

“Failed to upload 005_Clowns_Defeated_WalkAway.mp4: [POST] “https://alex-api.tpk.pw/api/resources”: Failed to fetch"

The Console Log in the browser shows

https://alex-api.tpk.pw/api/resources": <no response> Failed to fetch
    at async bl (DQlLeSm7.js:4:148284)
    at async Te (DQlLeSm7.js:4:149130)
    at async Proxy.post (CUjCRRl6.js:1:120)
    at async ae (IzeUTYAo.js:3:225)Caused by: TypeError: Failed to fetch
    at DQlLeSm7.js:4:69016
    at i.o [as raw] (DQlLeSm7.js:4:67774)
    at bl (DQlLeSm7.js:4:148297)
    at Te (DQlLeSm7.js:4:149136)
    at Proxy.post (CUjCRRl6.js:1:126)
    at Proxy.x (DQlLeSm7.js:4:83514)
    at ae (IzeUTYAo.js:3:233)
    at Ms (DQlLeSm7.js:2:21315)
    at dt (DQlLeSm7.js:2:21385)
    at W0 (DQlLeSm7.js:4:15398)

/content/images/2026/01/alex2-17.png

After a bit of thinking, the problem was obvious - I had set the timeouts but not the proxy body size in my Ingresses. For anything that needs “big” files, setting proxy body size to “0” is a must:

$ cat ./ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    nginx.org/websocket-services: alex-api-external-ip
  name: alex-apiingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-api.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-api-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-api.tpk.pw
    secretName: alex-api-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    nginx.org/websocket-services: alex-cdn-external-ip
  name: alex-cdningress
spec:
  ingressClassName: nginx
  rules:
  - host: alex-cdn.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-cdn-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex-cdn.tpk.pw
    secretName: alex-cdn-tls
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: azuredns-tpkpw
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    nginx.org/websocket-services: alex-external-ip
  name: alexingress
spec:
  ingressClassName: nginx
  rules:
  - host: alex.tpk.pw
    http:
      paths:
      - backend:
          service:
            name: alex-external-ip
            port:
              number: 80
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - alex.tpk.pw
    secretName: alex-tls

My ingress controller doesn’t handle updates too well, so I just removed the former Ingresses then re-added what you see above

$ kubectl delete ingress alexingress && kubectl delete ingress alex-cdningress && kubectl delete ingress alex-apiingress && sleep 5 && kubectl apply -f ./ingress.yaml
ingress.networking.k8s.io "alexingress" deleted
ingress.networking.k8s.io "alex-cdningress" deleted
ingress.networking.k8s.io "alex-apiingress" deleted
ingress.networking.k8s.io/alex-apiingress created
ingress.networking.k8s.io/alex-cdningress created
ingress.networking.k8s.io/alexingress created

Now video files and larger images upload without issue

/content/images/2026/01/alex2b-18.png

However, I only see errors when trying to view the files

/content/images/2026/01/alex2b-20.png

In docker, for the “CDN” I saw two key ports, 9001 and 9005:

/content/images/2026/01/alex2b-19.png

I tried editing the endpoints for both but neither solved the issue

builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl get endpoints | grep alex
alex-api-external-ip                                    192.168.1.142:8201                                                3h19m
alex-cdn-external-ip                                    192.168.1.142:9000                                                3h19m
alex-external-ip                                        192.168.1.142:8200                                                3h19m
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl edit endpoint alex-cdn-external-ip
error: the server doesn't have a resource type "endpoint"
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl edit endpoints alex-cdn-external-ip
endpoints/alex-cdn-external-ip edited
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl get endpoints | grep alex
alex-api-external-ip                                    192.168.1.142:8201                                                3h20m
alex-cdn-external-ip                                    192.168.1.142:9005                                                3h20m
alex-external-ip                                        192.168.1.142:8200                                                3h20m
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl edit endpoints alex-cdn-external-ip
endpoints/alex-cdn-external-ip edited
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl get endpoints | grep alex
alex-api-external-ip                                    192.168.1.142:8201                                                3h21m
alex-cdn-external-ip                                    192.168.1.142:9001                                                3h21m
alex-external-ip                                        192.168.1.142:8200                                                3h21m

The fix actually was three things:

  1. First, I had to realize that “RustFS” is that RustFS, the MinIO replacement. So the 9001 port is for console access (if enabled) and 9005 is to serve up files.
  2. The next issue was that when I was tweaking ports to test, I was changing “Endpoints” but neglected to change the Service which still had a targetPort set to 9000. When using a mix of clusterIP services and Endpoints to send traffic externally, one needs to keep those ports lined up.
  3. Lastly, I needed that “CDN_ENDPOINT” set to “/alexandrie/” (not “/” which I used). The reason is because this represents the “Bucket” the files live in.

I first fixed the endpoint/service

builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl delete endpoints alex-cdn-external-ip
endpoints "alex-cdn-external-ip" deleted
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl delete service alex-cdn-external-ip
service "alex-cdn-external-ip" deleted
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ vi fixit.yaml
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ cat ./fixit.yaml
---
apiVersion: v1
kind: Endpoints
metadata:
  name: alex-cdn-external-ip
subsets:
- addresses:
  - ip: 192.168.1.142
  ports:
  - name: alex-cdnint
    port: 9005
    protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: alex-cdn-external-ip
spec:
  clusterIP: None
  clusterIPs:
  - None
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  - IPv6
  ipFamilyPolicy: RequireDualStack
  ports:
  - name: alex-cdn
    port: 80
    protocol: TCP
    targetPort: 9005
  sessionAffinity: None
  type: ClusterIP
builder@DESKTOP-QADGF36:~/Workspaces/Alexandrie$ kubectl apply -f ./fixit.yaml
endpoints/alex-cdn-external-ip created
service/alex-cdn-external-ip created

But then corrected my .env file on the server and restarted with docker compose.

# ==============================================================================
# 1. PUBLIC ACCESS & GLOBAL URLS
# ==============================================================================

# Public URLs used by the client's browser to reach the services.
# If using a Reverse Proxy (Nginx/Traefik), use your public domain names here.
FRONTEND_URL=https://alex.tpk.pw
API_URL=https://alex-api.tpk.pw
CDN_URL=https://alex-cdn.tpk.pw

CDN_ENDPOINT=/alexandrie/ # The base path in the CDN where files are served from (you probably want to keep this as /alexandrie/ to match the bucket name, change it only if you changed the bucket name or if you have configured a reverse proxy path)

# ==============================================================================
# 2. APPLICATION FEATURE FLAGS
# ==============================================================================
# Enable or disable optional frontend features.

CONFIG_DISABLE_LANDING=false # Disable the landing page if set to true
CONFIG_DISABLE_SIGNUP=false # Disable the signup page if set to true

# ==============================================================================
# 3. BACKEND API — RUNTIME & SECURITY
# ==============================================================================

BACKEND_EXTERNAL_PORT=8201 # The port exposed on your host machine for the Backend API

COOKIE_DOMAIN=tpk.pw # localhost:8200 # Common domain for cookies (e.g., yourdomain.com if you have frontend.yourdomain.com and api.yourdomain.com)

Now a file works just fine to view:

/content/images/2026/01/alex2b-21.png

and

/content/images/2026/01/alex2b-22.png

The CDN uploaded images now show up when I am editing a post and clicking “add image”

/content/images/2026/01/alex2-23.png

And we see it just uses the CDN URL for the image

/content/images/2026/01/alex2b-24.png

There is a bug that shortly after uploading a video, the second or third attempt to view is blocked

/content/images/2026/01/alex2b-25.png

The Docker-compose uses the “latest” RustFS image and I see other users reporting similar errors

Since the release is just 3 days old as of this writing (alpha.81 which matches latest), I changed the docker compose to use the prior release (alpha.80)

builder@bosgamerz9:~/Alexandrie$ cat docker-compose.yml | grep rustfs
  rustfs:
    image: rustfs/rustfs:1.0.0-alpha.80
    container_name: alexandrie-rustfs
      - rustfs_data:/data
      - rustfs_logs:/logs
      MINIO_ENDPOINT: rustfs:9000
      rustfs:
  rustfs_data:
  rustfs_logs:

and restarted the containers

builder@bosgamerz9:~/Alexandrie$ docker compose down
[+] Running 5/5
 ✔ Container alexandrie-frontend          Removed                                                                                                                                                                                           0.3s
 ✔ Container alexandrie-backend           Removed                                                                                                                                                                                           0.3s
 ✔ Container alexandrie-rustfs            Removed                                                                                                                                                                                           2.0s
 ✔ Container alexandrie-mysql             Removed                                                                                                                                                                                           2.0s
 ✔ Network alexandrie_alexandrie-network  Removed                                                                                                                                                                                           0.2s
builder@bosgamerz9:~/Alexandrie$ vi docker-compose.yml
builder@bosgamerz9:~/Alexandrie$ docker compose up -d
[+] Running 8/8
 ✔ rustfs Pulled                                                                                                                                                                                                                            3.9s
   ✔ 1074353eec0d Already exists                                                                                                                                                                                                            0.0s
   ✔ efb5b00da6a9 Pull complete                                                                                                                                                                                                             0.6s
   ✔ d3ec0616f053 Pull complete                                                                                                                                                                                                             0.6s
   ✔ 981fcf90905c Pull complete                                                                                                                                                                                                             2.8s
   ✔ 82f2cf490642 Pull complete                                                                                                                                                                                                             2.8s
   ✔ 4f4fb700ef54 Pull complete                                                                                                                                                                                                             2.8s
   ✔ 1d1dee9383a6 Pull complete                                                                                                                                                                                                             2.8s
[+] Running 5/5
 ✔ Network alexandrie_alexandrie-network  Created                                                                                                                                                                                           0.1s
 ✔ Container alexandrie-rustfs            Healthy                                                                                                                                                                                           5.8s
 ✔ Container alexandrie-mysql             Healthy                                                                                                                                                                                           5.8s
 ✔ Container alexandrie-backend           Started                                                                                                                                                                                           5.9s
 ✔ Container alexandrie-frontend          Started              

I’m now having no issues with the video (e.g. a beating heart)

So while there isn’t a “video” markdown item, we can use HTML to add an embedded video to a post:

/content/images/2026/01/alex2b-26.png

Oddly, while the preview works

/content/images/2026/01/alex2b-27.png

The rendered page does not show the video

/content/images/2026/01/alex2b-28.png

Using the inspector in Firefox, it seems altogether removed

/content/images/2026/01/alex2b-29.png

Summary

Today we revisited Ghostty after a year and found it is quite solid. I am now using it as a daily driver in my Linux laptop (but not in my Windows gaming rig). I find the split view quite handy but I haven’t committed the key combinations to muscle memory just yet. I only wish I could do different UI themes like one can with the Mac OS terminal. I often like to distinguish between some various work threads by having different colour combinations of terminals.

The other suite we circled back on was Alexandrie. I’m still stewing on if I want to keep it. In our next post later this week we will be looking at Poznote and Joplin for some comparisons. In truth, none of the suites really handle video so I just need to decide if screen recordings are important to me.

I created a small survey form here if you wanted to give feedback.

alexandrie rustfs cdn ghostty docker kubernetes opensource

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes