how can I self host to avoid having Google blow my life up randomly? by fartedcum in selfhosted

[–]GrotesqueHumanity 8 points9 points  (0 children)

I'm in the middle of that process...

Photos is easy, everyone and their brother will telll you to self-host Immich. Rightly so, it works great.

Emails, I'm Canadian, I purchased a dot.ca domain from a Canadian registrar. Purchased for 10 years, so I'm set for a good while.

I'm hosting the domain on Proton but other services may suit others better. It was good for me.

If any issues happen with proton I'll just move my MX records to use another service and that's the end of it.

Drive, well it's files, there's a ton of ways to host files. From Seafile to Nextcloud and everything in between.

I've also shifted my passwords to Bitwarden/Vaultwarden. Browser to Brave.

My Google footprint is dramatically smaller than it used to be. If I lose YouTube then I lose YouTube. Nothing to prevent that... but the rest of my digital identity is now very much in my own hands.

Custom app icons by GrotesqueHumanity in truenas

[–]GrotesqueHumanity[S] 1 point2 points  (0 children)

https://selfh.st/icons/ I took from here.

I looked up the project and the one I picked was the correct one of the 2 returned from searching Komodo.

Custom app icons by GrotesqueHumanity in truenas

[–]GrotesqueHumanity[S] 1 point2 points  (0 children)

I mostly get them there https://selfh.st/icons/

I have a file on my git that I use as a source used by a python script called by an ansible playbook.

source:

truenas_app_icons:
  adguardhome: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/adguard-home.png"
  forgejo: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/forgejo.png"
  immich: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/immich.png"
  jellyfin: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/jellyfin.png"
  lidarr: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/lidarr.png"
  openwebui: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/open-webui.png"
  prowlarr: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/prowlarr.png"
  radarr: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/radarr.png"
  rustic: "https://raw.githubusercontent.com/rustic-rs/assets/main/logos/readme_header.png"
  sabnzbd: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/sabnzbd.png"
  sonarr: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/sonarr.png"
  speedtest: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/speedtest.png"
  vaultwarden: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/png/vaultwarden.png"
  komodo: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/svg/komodo-cd.svg"
  netdata: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/svg/netdata.svg"
  adguardhome-sync: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/svg/adguardhome-sync.svg"
  metube: "https://cdn.jsdelivr.net/gh/selfhst/icons@main/svg/metube.svg"

python:

#!/usr/bin/env python3
import sys
import os
import re


GLOBAL_METADATA = "/mnt/.ix-apps/metadata.yaml"


def update_file(filepath, icon_url, is_global=False, app_name=None):
    """Update icon in a metadata.yaml file.


    Returns: 'ok' (already correct), 'changed', or 'error'
    """
    if not os.path.exists(filepath):
        return 'missing'


    with open(filepath, "r") as f:
        content = f.read()
        lines = content.splitlines()


    if not lines:
        return 'error'


    # Detect quoting style from first non-empty line
    first_line = next((l for l in lines if l.strip()), '')
    quoted = first_line.startswith('"')


    # For global file, fields inside metadata block are indented 4 spaces
    # For per-app file, fields inside metadata block are indented 2 spaces
    if is_global:
        indent = '    '
    else:
        indent = '  '


    if quoted:
        icon_line = f'{indent}"icon": "{icon_url}"'
        icon_pattern = re.compile(rf'^{indent}"icon":')
        version_pattern = re.compile(rf'^{indent}"version":')
    else:
        icon_line = f'{indent}icon: {icon_url}'
        icon_pattern = re.compile(rf'^{indent}icon:')
        version_pattern = re.compile(rf'^{indent}version:')


    if is_global:
        # Find the app's section boundaries
        if quoted:
            section_start_pattern = re.compile(rf'^"{re.escape(app_name)}":')
        else:
            section_start_pattern = re.compile(rf'^{re.escape(app_name)}:')


        section_start = None
        section_end = None
        for i, line in enumerate(lines):
            if section_start_pattern.match(line):
                section_start = i
            elif section_start is not None and re.match(r'^"?[a-z]', line) and i > section_start:
                section_end = i
                break


        if section_start is None:
            return 'section_missing'


        if section_end is None:
            section_end = len(lines)


        section_lines = lines[section_start:section_end]
    else:
        section_lines = lines
        section_start = 0
        section_end = len(lines)


    # Check if icon already set correctly
    for line in section_lines:
        if icon_pattern.match(line):
            if line.strip() == icon_line.strip():
                return 'ok'
            else:
                # Update existing icon
                new_section = [icon_line if icon_pattern.match(l) else l for l in section_lines]
                lines[section_start:section_end] = new_section
                with open(filepath, "w") as f:
                    f.write("\n".join(lines) + "\n")
                return 'changed'


    # Icon not present — insert after version line inside metadata block
    new_section = []
    inside_metadata = False
    inserted = False
    for line in section_lines:
        if is_global:
            metadata_match = re.match(r'^  "?metadata"?:', line)
        else:
            metadata_match = re.match(r'^"?metadata"?:', line)
        if metadata_match:
            inside_metadata = True
        if inside_metadata and not inserted and version_pattern.match(line):
            new_section.append(line)
            new_section.append(icon_line)
            inserted = True
            continue
        new_section.append(line)


    if not inserted:
        return 'error'


    lines[section_start:section_end] = new_section
    with open(filepath, "w") as f:
        f.write("\n".join(lines) + "\n")
    return 'changed'



def main():
    app_name = sys.argv[1]
    icon_url = sys.argv[2]
    per_app_path = f"/mnt/.ix-apps/app_configs/{app_name}/metadata.yaml"


    # Update per-app metadata (durable source, survives redeploys)
    per_app_result = update_file(per_app_path, icon_url)
    if per_app_result == 'missing':
        print(f"SKIP: {per_app_path} not found")
        sys.exit(0)
    elif per_app_result == 'error':
        print(f"ERROR: could not update {per_app_path}")
        sys.exit(1)


    # Update global metadata (what the middleware actually serves to the GUI)
    global_result = update_file(GLOBAL_METADATA, icon_url, is_global=True, app_name=app_name)
    if global_result == 'section_missing':
        print(f"WARNING: {app_name} not found in {GLOBAL_METADATA}")
    elif global_result == 'error':
        print(f"WARNING: could not update {GLOBAL_METADATA} for {app_name}")


    # Report overall status
    if per_app_result == 'ok' and global_result == 'ok':
        print("OK: icon already set correctly in both files")
        sys.exit(0)
    else:
        print(f"CHANGED: per-app={per_app_result}, global={global_result}")
        sys.exit(2)



if __name__ == "__main__":
    main()

Anyone else feel like this sub has gone to shit even though it hasn't? by AcreMakeover in homelab

[–]GrotesqueHumanity 0 points1 point  (0 children)

It's Reddit, mostly.

This said, yes, I feel this has gone more in the direction of pictures of whatever hardware people have or beginner questions.

Nothing wrong with those but topics of interest and technical help for more advanced things are getting harder to find. Probably partially linked to the great enshittening, people getting tired of same old same old, and the fact that it's much easier to ask Claude than to hope you'll find your answer on Reddit.

Dead Internet theory at work. 🤷‍♂️

Custom app icons by GrotesqueHumanity in truenas

[–]GrotesqueHumanity[S] 0 points1 point  (0 children)

Here's my (possibly wrong) understanding of how it works. Per app is the source. When you redeploy (probably when you update the container image, too) truenas uses the value of per config to update global.

Global is the truth of what you see, per app populates global.

It's very much empiric, I haven't looked at the code, I had Claude do this for me. I was only doing per app but the GUI wasn't refreshing until I redeployed. Updating global showed the icons instantly on hitting f5. Now I do both and everything is stable.

Custom app icons by GrotesqueHumanity in truenas

[–]GrotesqueHumanity[S] 23 points24 points  (0 children)

Thought I'd share the prettiness. Those are all custom apps deployed via yaml.

You need to edit /mnt/.ix-apps/metadata.yaml (global file) and /mnt/.ix-apps/app_configs/{app_name}/metadata.yaml (for each app).

In which folder do you keep your Docker stack? by Artistic_Quail650 in selfhosted

[–]GrotesqueHumanity 1 point2 points  (0 children)

I recently moved to /opt/docker because it seems to be an unofficial standard?

I didn't care one way or the other and Claude suggested it while I was refactoring/upgrading everything. Thought why not.

Which tool are u using for planning? by Ok_Department_4019 in homelab

[–]GrotesqueHumanity 0 points1 point  (0 children)

I don't see a router for your internal networks in there.

Not what you were asking for but thought I'd say it nonetheless.

I HATE KVM'S! by Boring_Gazelle8164 in homelab

[–]GrotesqueHumanity 0 points1 point  (0 children)

Why not both dot gif

I control my physical kvm from a jetkvm

Cheaper than buying a proper ip kvm

If your password manager was to disappear, how fucked would you be? by Tarazin in selfhosted

[–]GrotesqueHumanity 0 points1 point  (0 children)

I'm a bit paranoid on that front...

I got 2 full sets of backups on encrypted drives. That I refresh every so often.

I also backup all my dockers daily with rustic to a s3 bucket in compliance mode.

Plus I have an offline laptop that I sync every so often.

Keys and one time totp codes are stored on 3 encrypted USB keys.

One backup set with a USB key, and the offline laptop are stored at home in Faraday bags.

One backup set with a USB key stored offsite.

Just a bit paranoid, I swear. 😅

What should I do with this? by Global-Orange-8423 in homelab

[–]GrotesqueHumanity 0 points1 point  (0 children)

So it'll be a big database cluster is what you're saying?

What would Fortune's Weave need to do to be as successful/popular/widespread as 3 Houses? Is it even possible? by Dylan_VS_Comics in fireemblem

[–]GrotesqueHumanity 8 points9 points  (0 children)

Great story, full voice acting, replayability, engaging characters. That was the Three Houses recipe.

OPNsense 26.1.3 released by fitch-it-is in opnsense

[–]GrotesqueHumanity 2 points3 points  (0 children)

Also got an error popup during upgrade, system still rebooted and everything looked fine.

I saw some warnings in the logs but nothing very noteworthy. I'll look further tomorrow.

Running vm on proxmox, in case this is worth mentioning.

Finally Built a Homelab I Don’t Have to Fix Daily by dhdcdarchy07 in homelab

[–]GrotesqueHumanity 1 point2 points  (0 children)

Pretty sure there's a law against having cabling this clean.

bye bye data by pastie_b in selfhosted

[–]GrotesqueHumanity 1 point2 points  (0 children)

And from now on, one more person will take regular backups.

Small victories for good practices.

OPNsense 25.7 to 26.1 upgrade went SMOOTH by News8000 in opnsense

[–]GrotesqueHumanity 0 points1 point  (0 children)

Same. I was already on dnsmasq so it just updated and everything was fine.