Suranyami

Polyglot developer, geometric tessellation fan, ambient DJ.

These last few years, I've been trying out a Kamado Joe smoker.

It's been challenging.

There are a number of things that make them completely different to a normal barbecue. I previously had a simple Weber barbecue. It worked fine for hot and fast cooking and after a bit of burn-down and ash-over, you could easily do a few low & slow things as long as you kept your expectations within an hour or two. There was no thermal insulation to speak of, except for a dome of steel with an adjustable outlet.

A smoker of the sort similar to a Kamado Joe (there are plenty of them…) is a different animal all together.

The first thing to know is this: refractory bricks stay hot for up to 24 hours.

The insides of these types of smokers are modelled similarly to the best pizza ovens and metal refineries. Heat dissipates, and that's bad, if you need to make something hot. It means you need to keep supplying fuel to keep things hot.

That's where refractory bricks come in: they reflect heat back from where they came from.

The outside of a Kamado Joe needs to be running for quite some time before it even gets slightly hot, because of these bricks.

This presents 2 problems:

  1. If I want something to cook low and slow for a long time, I need lots of fuel.
  2. If I ignite a lot of fuel, everything will get very hot and generally have a texture like leather.

So what's the solution?

I found 2 helpful procedures that, after experimenting with them, I can verify they do help make things a lot easier:

  1. The “minion method”. This entails making a semi-circular chain of charcoal with smoking chunks positioned above each section, such that when lit at one end, it slowly burns through the entire chain, a bit like a very slow fuse.

  2. A tray of water. This is so obvious in retrospect! Ideally, the internal temperature should be in the 120-150°C range. Water will have a beneficial feedback effect: too hot and the steam will suppress the burning, too low and it won't do much except act as thermal inertia.

A combination of the 2 above hints, allowing the first burn to properly ash over, and not moving the air inlets/outlets too chaotically has led to much more predictable outcomes.

Today's bounty was:

  • Totally bodaciously tender pork shoulder with brown sugar, mustard and left-over Nong-Shim Ramyun spicy instant noodle powder rub.
  • Tender-as roast chicken from the last 2 hours of the above pork cook, prepped with lemon zest, smoked paprika and oregano rub with olive oil and smoked salt.
  • Purple Japanese sweet potatoes wrapped in foil with olive oil, pepper and salt.
  • Smoked long, sweet peppers
  • Baked and smoked aubergine.

One of the things that made all of the above much, much easier was having a decent temperature probe. I have a Meater probe, and it's worth every cent.

Discuss...

Create a network for nginx-proxy-manager to use:

docker network create nginx-proxy-manager-network

Then add this to all the compose files that want to be referenced by Nginx-Proxy-Manager:

networks:
  nginx-proxy-manager-network:
    external: true

Now you can use the service name as the host in the Nginx-Proxy-Manager GUI:

Source Destination
awwesome.suranyami.com http://awwesome:8088

Discuss...

My current Homelab server rack:

  • Turing Pi 2 cluster, now with 4 x RK1 3588 8-core arm_64 CPUs with 32GB RAM each (not shown in pictures below), 6 TOPS NPU, in a 2U Silverstone rack-mount case, fans, 8-bay hot-swappable SSDs, 4TB SSDs x 4
  • a bunch or Raspberry Pi 4s (4GB x 2, 8GB x 1, 1TB NVMe SSDs in Argon One cases)
  • Radxa Rockchip 5B 16GB RAM, 4TB NVMe. 8-core arm_64, 6 TOPS NPU + case
  • Argon Eon, RasPi 4, 4GB RAM 4 x 4TB SSD
  • Radxa Penta NAS kit with RockChip 4 4GB + 4 x 4TB SATA SSDs
  • UPS + surge protection, because we had a power surge during a storm here a few months ago that destroyed one of the RasPi CM4s I had in the Turing Pi 2, a power supply, and a 15” portable LCD monitor… expensive power surge! It’s the 21st century… you’d think these things wouldn’t happen any more, but they do.

This is all instrumented by a Portainer/Docker Swarm setup running:

  • Nginx-Proxy-Manager (Simple reverse proxy manager)
  • DuckDNS (Dynamic DNS)
  • Minecraft Server (suranyami.duckdns.org:25565)
  • Awwesome Self-Hosted Browser link (no login required)
  • Excalidraw (FOSS collaborative drawing webapp, no login required)
  • Homarr (Home page for all my services)
  • Home Assistant (IoT control for smart devices)
  • Jackett
  • Joplin Server (Knowledge Base)
  • Netdata monitoring on most nodes
  • Ollama + Ollama WebUI (really slow ATM… installing NPU drivers this weekend, coz the RockChip 3588s have 6 TOPS of neural processing)
  • Overseer
  • Plex Media Server
  • Radarr
  • Sonarr
  • Tautulli
  • Tdarr
  • IT-Tools (No login needed). Check it out! It’s very useful!
  • Transmission (Torrents)
  • Uptime-Kuma (Uptime monitoring)
  • WG-Easy Wireguard VPN management
  • GlusterFS distributed File System with 2 x redundancy, 1 unified storage volume of 18TB in total

Discuss...

Today I needed to different layouts for public-facing and authorised pages in a LiveView app.

After an annoying amount of digging in documentation and forums, the following was the most elegant solution I found.

Assume we have these layouts in myapp/lib/myapp_web/components/layouts/:

authenticated.html.heex
public.html.heex

And, also assuming that there is something different in each layout: stuff you can't use unless signed in.

In lib/myapp_web/router.ex modify these authentication routes to add a layout: in the live_session statements:


  scope "/", MyappWeb do
    pipe_through [:browser, :redirect_if_user_is_authenticated]

    live_session :redirect_if_user_is_authenticated,
      on_mount: [{MyappWeb.UserAuth, :redirect_if_user_is_authenticated}],
      layout: {MyappWeb.Layouts, :public} do
      live "/users/register", UserRegistrationLive, :new
      live "/users/log_in", UserLoginLive, :new
      live "/users/reset_password", UserForgotPasswordLive, :new
      live "/users/reset_password/:token", UserResetPasswordLive, :edit
    end

    post "/users/log_in", UserSessionController, :create
  end

  scope "/", ReflectalWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{MyappWeb.UserAuth, :ensure_authenticated}],
      layout: {MyappWeb.Layouts, :authenticated} do
      live "/dashboard", DashboardLive.Show, :show
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
    end
  end

The important lines here are:

      layout: {MyappWeb.Layouts, :public} do

and

      layout: {MyappWeb.Layouts, :authenticated} do

Simple!

Discuss...

Here's a top tip to avoid:

Ironbark smoke is abso-fucking-lutely vile. Do not use a couple of ironbark logs in your smoker unless they have completely burnt down to embers and can be used to ignite something that doesn’t make everything taste like sadness and other people’s headaches.

I just managed to ruin an entire barbecue this way. Learn from my stupidity: just never do this.

Discuss...

Countries

Language

(ni) ほん (hon)(go) Japanese (language)


えい (ei)(go) English (language)

Nationality

(ni) ほん (hon) じん ( jin) Japanese (person)
えい (ei)(go)
English (language)

Nationality

(ni) ほん (hon) じん ( jin)
Japanese (person)

Discuss...

I had a bit of a mystery that was bugging me for ages. After enabling automatic formatting for Elixir files in VSCode, I was getting ugly highlighting that looked like this:

Ugly highlighting

It turns out this is a feature of the RainbowIndents extension to show you where you have inconsistent indenting (e.g. 3 spaces, when everything else is 2).

The problem with this is mix format has unchangeable opinions about indenting, such as in a with statement, where it cares more about alignment than even/odd indent boundaries.

The fix is to exclude indentation errors for Elixir. In your settings.json add this:

This was the fix.

With that in place, we now get plain ol' rainbow indenting:

Fixed!

Discuss...

This first appeared in a forum post on community.fly.io

I've updated it a bit with some clarifications and a fix for a version-clash issue I had.

Setup the project

$ mix phx.new hello  --umbrella --no-ecto --install
$ cd hello_umbrella

Enable server in config/runtime.exs:

config :hello_web, HelloWeb.Endpoint, server: true

Add elixir_buildpack.config with the preferred Erlang and Elixir versions:

$ cat <<EOF>./elixir_buildpack.config
elixir_version=1.14
erlang_version=24.3
EOF

I had problems with too-specific versions in the above, so just use major versions, e.g. elixir_version=1.14 versus elixir_version=1.14.3-otp-24

Deploy the assets from within the web app directory:

$ cd apps/hello_web
$ MIX_ENV=prod mix assets.deploy
$ cd ../../

Setup the Fly stuff

$ fly launch

Answer “no” to deploy request and leave fly launch, then set up the secret:

$ export SECRET_KEY_BASE=$(mix phx.gen.secret)
$ fly secrets set SECRET_KEY_BASE=$SECRET_KEY_BASE

Deploy:

$ fly deploy

Enjoy your app!

$ fly open

and the umbrella application is finally happily up and running!

Summary

Besides the steps above, here are the summary of the current differences in how Fly handles deployment between plain and umbrella.

to deploy an Umbrella via Fly:

  • you have to explicitly set server: true in config/runtime.exs
  • you have to add the required elixir_buildpack.config file to the project
  • you have to build the application assets
  • you have to set the SECRET_KEY_BASE executing fly secrets after fly launch and before fly deploy (you can’t just export it to an environment variable)
  • the Fly builder will not generate a Docker file as is the case with plain apps (but if you supply one, the builder will use it)

Discuss...

Install iperf

on MacOS:

brew install iperf

on Linux:

sudo apt install iperf -y

Start iperf on the server:

iperf -s

This waits for incoming connections from clients. Designate another machine as a client and run this command, substituting the IP address of your server machine for the sample one here:

iperf -c 192.168.1.2

Results

Setup was:

LAN config

Results were:

> iperf -c 192.168.1.238
------------------------------------------------------------
Client connecting to 192.168.1.238, TCP port 5001
TCP window size:  128 KByte (default)
------------------------------------------------------------
[  1] local 192.168.1.91 port 58359 connected with 192.168.1.238 port 5001 (icwnd/mss/irtt=11/1448/12000)
[ ID] Interval       Transfer     Bandwidth
[  1] 0.00-10.28 sec  63.9 MBytes  52.1 Mbits/sec

Discuss...

When setting up profiles for AWS locally, start with your own credentials as default, then add profile entries for the different environments. e.g.

# ~/.aws/config

[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[profile staging]
role_arn = arn:aws:iam::0987654321:role/staging
source_profile = default
region = ap-southeast-2
output = json

[profile prod]
role_arn = arn:aws:iam::5678901234:role/prod
source_profile = default
region = ap-southeast-2
output = json

Discuss...

Enter your email to subscribe to updates.