Dispositivos#

PC Master Race

  • OS: Kubuntu 26.04 LTS
  • CPU: AMD Ryzen 5 3600
  • GPU: AMD Radeon RX 6800 16 GB
  • RAM: 32 GB (4×8 GB GeIL Super Luce DDR4 3200 MHz)
  • NVMe: 1 TB (2×512 GB Adata XPG Spectrix S40G)
  • Motherboard: ASUS TUF Gaming X570-PRO (Wi-Fi)
  • Mouse: Logitech G305
  • Teclado: HyperX Alloy Origins Core con keycaps Razer Pink PBT
  • Auriculares: Audio-Technica ATH-M50x con FiiO BTA10 y Sony Inzone H9

Raspberry Pi 4 Model B

Apple MacBook Air M1 2020

Samsung Galaxy S22 Ultra

Instalación base#

Kubuntu 26.04 instalado en modo UEFI con:

  • Btrfs
  • Swap file
  • LUKS activado

Layout: subvolúmenes /@, /@home y /@swap, swap file en /swap/swapfile, disco cifrado con LUKS.

BIOS#

  • Cargar valores predeterminados optimizados
  • Configurar RAM a 3200 MHz con DOCP/XMP
  • Habilitar Above 4G Decoding
  • Habilitar Resizable BAR
  • Habilitar SVM Mode / AMD-V
  • Habilitar Secure Boot
  • Deshabilitar CSM
  • Configurar los coolers para que hagan el menor ruido posible

Linux#

GRUB#

for p in preempt=full pcie_aspm=off; do
  grep -q "$p" /etc/default/grub || sudo sed -i "s/GRUB_CMDLINE_LINUX_DEFAULT=\([\"']\)\(.*\)\1/GRUB_CMDLINE_LINUX_DEFAULT=\1\2 $p\1/" /etc/default/grub
done && sudo update-grub
  • preempt=full - menor latencia de scheduling.
  • pcie_aspm=off - workaround para WiFi Intel AX200 trabada en D3cold.
  • No uso quiet porque prefiero ver más info al bootear.
  • cryptdevice=... y root=... dependen de cada instalación.

LUKS performance#

sudo dmsetup table

sudo cryptsetup --perf-no_read_workqueue --perf-no_write_workqueue --allow-discards --persistent refresh luks-blablabla
  • no_read_workqueue / no_write_workqueue - menos latencia en NVMe.
  • allow-discards - habilita TRIM en SSD.

Btrfs mounts#

Kubuntu ya crea los subvolúmenes y el swap file. /tmp ya viene en tmpfs por systemd. Solo cambio opciones de mount:

sudo nvim /etc/fstab

En / y /home, sacar autodefrag si está y agregar compress=zstd:

/dev/mapper/luks-blablabla /     btrfs subvol=/@,defaults,noatime,compress=zstd 0 0
/dev/mapper/luks-blablabla /home btrfs subvol=/@home,defaults,noatime,compress=zstd 0 0
  • noatime - menos escrituras.
  • compress=zstd - compresión transparente.

sysctl#

sudo tee /etc/sysctl.d/99-performance.conf > /dev/null << 'EOF'
kernel.nmi_watchdog = 0
kernel.watchdog = 0
net.ipv4.tcp_fastopen = 3
vm.dirty_ratio = 10
vm.dirty_background_ratio = 5
EOF

sudo tee /etc/sysctl.d/99-vm-zram.conf > /dev/null << 'EOF'
vm.swappiness = 150
vm.vfs_cache_pressure = 50
vm.page-cluster = 0
vm.watermark_scale_factor = 100
vm.compaction_proactiveness = 50
EOF

sudo sysctl --system

zram#

sudo apt install systemd-zram-generator && \
  sudo tee /etc/systemd/zram-generator.conf > /dev/null << 'EOF'
[zram0]
zram-size = ram / 2
compression-algorithm = zstd
swap-priority = 100
EOF

sudo systemctl daemon-reload && sudo systemctl start dev-zram0.swap

Swap file Btrfs#

sudo swapoff /swap/swapfile && \
  sudo rm -f /swap/swapfile && \
  sudo btrfs filesystem mkswapfile --size 4G /swap/swapfile && \
  sudo swapon /swap/swapfile

El swap en disco queda como fallback cuando zram se llena.

OOM#

sudo apt install systemd-oomd && \
  sudo systemctl enable --now systemd-oomd.service

CPU#

powerprofilesctl set performance
  • amd-pstate active + governor performance + EPP performance
  • NVMe scheduler none ya es default normal para NVMe.

Kernel XanMod#

sudo install -d -m 0755 /etc/apt/keyrings
wget -qO - https://dl.xanmod.org/archive.key | sudo gpg --dearmor -vo /etc/apt/keyrings/xanmod-archive-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/xanmod-archive-keyring.gpg] http://deb.xanmod.org $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/xanmod-release.list
sudo apt update && sudo apt install linux-xanmod-x64v3

WiFi Intel AX200#

sudo tee /etc/modprobe.d/iwlwifi-fix.conf > /dev/null << 'EOF'
options iwlwifi power_save=0
options iwlmvm power_scheme=1
EOF
sudo tee /etc/NetworkManager/conf.d/99-disable-wifi-powersave.conf > /dev/null << 'EOF'
[connection]
wifi.powersave=2
EOF

sudo systemctl restart NetworkManager

SDDM AMDGPU#

Solo KDE. Solución oficial para pantalla negra al bootear (regresión en 26.04, LP: #2063143).

sudo mkdir -p /etc/systemd/system/sddm.service.d && \
  sudo tee /etc/systemd/system/sddm.service.d/udev-settle.conf > /dev/null << 'EOF'
[Unit]
After=systemd-udev-settle.service
Wants=systemd-udev-settle.service
EOF

sudo systemctl daemon-reload

NetworkManager#

sudo systemctl disable --now NetworkManager-wait-online.service
sudo tee /etc/NetworkManager/conf.d/99-mac-address-policy.conf > /dev/null << 'EOF'
[device]
wifi.scan-rand-mac-address=yes

[connection]
wifi.cloned-mac-address=stable
ethernet.cloned-mac-address=preserve
EOF

sudo systemctl restart NetworkManager

Bluetooth reset#

sudo rfkill unblock all
sudo systemctl restart bluetooth
sudo modprobe -r btusb
sudo modprobe btusb

Límites de inotify#

Aumentar instancias de file watchers para IDEs y herramientas de desarrollo:

echo "fs.inotify.max_user_instances = 1024" | sudo tee /etc/sysctl.d/90-inotify.conf
sudo sysctl --system

Paquetes#

apt#

sudo apt install \
  7zip adb antiword aria2 aspell-es atuin audacity autoconf automake axel bat \
  bear ble.sh bleachbit brightnessctl btop build-essential buildah \
  ca-certificates cabextract clamav clang cmake cmatrix cockpit cockpit-podman cowsay \
  criu curl ddcui ddcutil diffoscope direnv distrobox dnsutils duf \
  editorconfig expect eza fastboot fcitx5-mozc fd-find ffmpeg ffmpegthumbnailer filelight \
  firejail flatpak fortune-mod fzf gamemode gdb gh ghostty gifsicle \
  git glab gnupg golang-go gwenview handbrake hashcat httpie hugo \
  hunspell-en-us hunspell-es hw-probe hyperfine hyphen-en-us hyphen-es \
  inotify-tools iotop-c isoimagewriter jo jq just kcalc kde-config-flatpak \
  lazygit libfuse-dev libfuse3-dev libtool libvirt-daemon-system lolcat \
  magic-wormhole meson moreutils mpv mythes-en-us mythes-es ncdu needrestart \
  neovim nethogs ninja-build nload nmap nvtop okular openrgb optipng pamixer \
  pandoc pdfgrep pipx pkg-config plasma-discover-backend-flatpak playerctl \
  pngquant podman podman-docker podman-toolbox poppler-utils pre-commit procs \
  python-is-python3 python3 python3-dev python3-full python3-venv \
  qemu-system-x86 redis-server redis-tools ripgrep-all shellcheck shfmt sl \
  speedtest-cli ssh sshpass starship tealdeer thefuck tidy timeshift tmux \
  toilet torbrowser-launcher trash-cli tree tshark ufw ugrep universal-ctags \
  unrar unzip valgrind virt-manager vlc wget whois wireshark xmlstarlet ydotool yt-dlp \
  zoxide
ln -s "$(command -v fdfind)" ~/.local/bin/fd

Permisos de usuario#

sudo usermod -aG kvm,libvirt,wireshark "$USER"

Cerrar sesión y volver a entrar.

ROCm#

sudo apt install rocm rocm-podman-support && \
  sudo usermod -aG render,video "$USER"

Cerrar sesión y volver a entrar.

APT security auto-updates#

sudo apt install unattended-upgrades && \
  sudo tee /etc/apt/apt.conf.d/20auto-upgrades > /dev/null << 'EOF'
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";
EOF

Ubuntu Pro#

Obtener token en https://ubuntu.com/pro/dashboard.

sudo pro attach <TU_TOKEN>
pro status

Repos externos#

extrepo#

extrepo es el gestor de repos externos de Debian. En vez de bajar scripts de Internet y correrlos como root, usás un catálogo curado donde ya están las claves GPG y las definiciones de los repos. Buscás con extrepo search, habilitás con extrepo enable.

sudo apt install extrepo
sudo extrepo enable brave_release google_chrome librewolf steam tailscale vscode
sudo apt update
sudo apt install brave-browser code google-chrome-stable librewolf steam tailscale
sudo tailscale up

Gestores y runtimes#

Homebrew#

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \
  eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" && \
  brew install anomalyco/tap/opencode codex croc fnm gemini-cli topgrade uv yq

Entorno de usuario systemd#

mkdir -p ~/.config/environment.d

cat > ~/.config/environment.d/10-user-path.conf << 'EOF'
ANDROID_HOME=$HOME/Android/Sdk
ANDROID_SDK_ROOT=$ANDROID_HOME
BUN_INSTALL=$HOME/.bun
HOMEBREW_CELLAR=/home/linuxbrew/.linuxbrew/Cellar
HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew
HOMEBREW_REPOSITORY=/home/linuxbrew/.linuxbrew/Homebrew
INFOPATH=/home/linuxbrew/.linuxbrew/share/info:${INFOPATH:-}
PATH=$HOME/.cargo/bin:$HOME/.local/share/pnpm:/home/linuxbrew/.linuxbrew/sbin:/home/linuxbrew/.linuxbrew/bin:$HOME/.bun/bin:$HOME/Android/Sdk/platform-tools:$HOME/Android/Sdk/emulator:$HOME/Android/Sdk/cmdline-tools/latest/bin:$HOME/.local/bin:$HOME/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
PNPM_HOME=$HOME/.local/share/pnpm
EOF

systemctl --user daemon-reload

Topgrade auto-update#

mkdir -p ~/.config ~/.config/systemd/user

cat > ~/.config/topgrade.toml << 'EOF'
[misc]
assume_yes = true
cleanup = true
no_retry = true
notify_end = "on_failure"
disable = ["snap", "restarts", "clam_av_db"]
EOF

cat > ~/.config/systemd/user/topgrade.service << 'EOF'
[Unit]
Description=Update packages with Topgrade

[Service]
Type=oneshot
ExecStart=/home/linuxbrew/.linuxbrew/bin/topgrade --disable system snap restarts clam_av_db
EOF

cat > ~/.config/systemd/user/topgrade.timer << 'EOF'
[Unit]
Description=Run Topgrade automatically

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1h

[Install]
WantedBy=timers.target
EOF

systemctl --user daemon-reload && systemctl --user enable --now topgrade.timer

pnpm global#

eval "$(fnm env --use-on-cd --shell bash)" && \
  fnm install --lts --use && \
  fnm default "$(fnm current)" && \
  corepack install --global pnpm@latest && \
  mkdir -p ~/.local/share/pnpm && \
  pnpm config set global-bin-dir ~/.local/share/pnpm --location=global

Seguridad npm / pnpm#

Hardening contra supply chain attacks: bloquear scripts de instalación y evitar paquetes recién publicados.

# npm: no ejecutar scripts de terceros
cat > ~/.npmrc << 'EOF'
ignore-scripts=true
EOF

# pnpm: rechazar paquetes publicados hace menos de 1 día
pnpm config set minimumReleaseAge 1440 --location=global

# bun: bloquear scripts y paquetes recién publicados
cat > ~/.bunfig.toml << 'EOF'
[install]
ignoreScripts=true
minimumReleaseAge=86400
EOF

Con esto, npm no ejecuta preinstall ni postinstall de dependencias. pnpm espera 1 día antes de aceptar paquetes nuevos (1440 minutos), y bun hace lo mismo con 86400 segundos. pnpm 11+ ya trae defensas para este tipo de ataques.

Scripts#

# Bun
curl -fsSL https://bun.sh/install | bash

# Rust / Cargo
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Apps#

Nerd Fonts#

brew install --cask font-hack-nerd-font font-ubuntu-mono-nerd-font && fc-cache -fv

Flatpak#

flatpak remote-add --if-not-exists flathub \
  https://flathub.org/repo/flathub.flatpakrepo

flatpak install flathub \
  com.github.wwmm.easyeffects \
  com.github.PintaProject.Pinta com.github.tchx84.Flatseal \
  com.obsproject.Studio com.obsproject.Studio.Plugin.OBSVkCapture//stable \
  com.spotify.Client com.stremio.Stremio com.usebottles.bottles \
  com.vysp3r.ProtonPlus dev.vencord.Vesktop io.github.flattool.Warehouse \
  io.github.hedge_dev.hedgemodmanager io.podman_desktop.PodmanDesktop \
  it.mijorus.gearlever net.lutris.Lutris net.retrodeck.retrodeck \
  org.freedesktop.Platform.VulkanLayer.OBSVkCapture//25.08 org.gimp.GIMP \
  org.kde.kdenlive org.kde.krita org.kde.yakuake org.libreoffice.LibreOffice \
  org.localsend.localsend_app org.qbittorrent.qBittorrent org.signal.Signal \
  org.telegram.desktop

Android Studio#

sudo snap install android-studio --classic

El Setup Wizard descarga el SDK en ~/Android/Sdk.

Zed#

curl -f https://zed.dev/install.sh | sh

Codex Desktop#

Build Linux no oficial de Codex Desktop desde el DMG oficial de macOS: https://github.com/ilysenko/codex-desktop-linux.

Instalar con paquete nativo (.deb en Kubuntu/Ubuntu), Computer Use UI, Zed opener y remote/mobile activados:

sudo apt install ydotool xdg-desktop-portal-kde
sudo usermod -a -G input "$USER"
systemctl --user enable --now ydotool.service
flatpak permission-set kde-authorized remote-desktop "" yes

git clone https://github.com/ilysenko/codex-desktop-linux.git ~/Documents/codex-desktop-linux
cd ~/Documents/codex-desktop-linux

cat > linux-features/features.json << 'EOF'
{
  "enabled": [
    "open-target-discovery",
    "zed-opener",
    "remote-control-ui",
    "remote-mobile-control"
  ]
}
EOF

mkdir -p ~/.config/codex-desktop
echo '{"codex-linux-computer-use-ui-enabled": true}' > ~/.config/codex-desktop/settings.json

make bootstrap-native

make bootstrap-native instala dependencias, descarga el Codex.dmg, genera codex-app/, arma el paquete nativo y lo instala. Si las dependencias ya existen, usar make install-native.

Para que Computer Use pueda mandar input, cerrá sesión y volvé a entrar después de agregar tu usuario al grupo input.

El permiso kde-authorized evita el popup de Remote Control de KDE para apps host sin app_id. Es cómodo, pero amplio.

Trezor Suite#

Descargar Trezor Suite como AppImage y manejarlo con Gear Lever.

Timeshift#

sudo timeshift-gtk

Config:

  • Tipo: Btrfs
  • Ubicación: mismo disco Btrfs del sistema
  • Schedule: diario + boot
  • Mantener: 3 diarios, 3 boot, 2 semanales
  • /home: no incluir datos de usuario

Shell y terminal#

Ghostty#

mkdir -p ~/.config/ghostty && \
  tee ~/.config/ghostty/config.ghostty > /dev/null << 'EOF'
background-opacity = "0.9"
font-family = "UbuntuMono Nerd Font"
font-size = "14"
theme = "Dark Pastel"
window-height = "32"
window-width = "100"
EOF

profile#

~/.profile:

# path helper
path_prepend() {
  [ -d "$1" ] || return
  case ":$PATH:" in
    *":$1:"*) ;;
    *) export PATH="$1:$PATH" ;;
  esac
}

# local bin
path_prepend "$HOME/bin"
path_prepend "$HOME/.local/bin"

# android sdk
export ANDROID_HOME="$HOME/Android/Sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
path_prepend "$ANDROID_HOME/cmdline-tools/latest/bin"
path_prepend "$ANDROID_HOME/emulator"
path_prepend "$ANDROID_HOME/platform-tools"

# bun
export BUN_INSTALL="$HOME/.bun"
path_prepend "$BUN_INSTALL/bin"

# homebrew
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then
  export HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew"
  export HOMEBREW_CELLAR="$HOMEBREW_PREFIX/Cellar"
  export HOMEBREW_REPOSITORY="$HOMEBREW_PREFIX/Homebrew"
  path_prepend "$HOMEBREW_PREFIX/bin"
  path_prepend "$HOMEBREW_PREFIX/sbin"
  [ -z "${MANPATH-}" ] || export MANPATH=":${MANPATH#:}"
  export INFOPATH="$HOMEBREW_PREFIX/share/info:${INFOPATH:-}"
fi

# pnpm
export PNPM_HOME="$HOME/.local/share/pnpm"
path_prepend "$PNPM_HOME"

# rust/cargo
[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
	. "$HOME/.bashrc"
    fi
fi

bashrc#

~/.bashrc:

# ble.sh - load first, attach last
[[ $- == *i* && -f /usr/share/blesh/ble.sh ]] && source -- /usr/share/blesh/ble.sh --attach=none

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

HISTCONTROL=ignoreboth:erasedups
shopt -s histappend
HISTSIZE=100000
HISTFILESIZE=100000
shopt -s checkwinsize
shopt -s globstar
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# PS1 is handled by starship (see below)

if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias grep='grep --color=auto'
    alias egrep='grep -E --color=auto'
    alias fgrep='grep -F --color=auto'
fi

# aliases
alias ls='eza'
alias ll='eza -l'
alias la='eza -la'
alias cat='batcat --paging=never'
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

# fnm
command -v fnm >/dev/null && eval "$(fnm env --use-on-cd --shell bash)"

# starship
command -v starship >/dev/null && eval "$(starship init bash)"

# thefuck - lazy load
fuck() { unset -f fuck; eval "$(thefuck --alias)"; fuck "$@"; }

# fzf
command -v fzf >/dev/null && eval "$(fzf --bash)"

# zoxide
command -v zoxide >/dev/null && eval "$(zoxide init --cmd cd bash)"

# atuin
if command -v atuin >/dev/null; then
  if [[ ${BLE_VERSION-} ]]; then
    eval "$(atuin init bash --disable-up-arrow)"
    ble-bind -x 'C-r' '__atuin_history'
  else
    eval "$(atuin init bash)"
  fi
fi

# ble.sh attach
[[ ! ${BLE_VERSION-} ]] || ble-attach

Servicios y red#

Podman socket#

systemctl --user enable --now podman.socket

OpenCode server#

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/opencode-serve.service << 'EOF'
[Unit]
Description=OpenCode serve
After=graphical-session.target
PartOf=graphical-session.target

[Service]
Type=simple
ExecStart=/home/linuxbrew/.linuxbrew/bin/opencode serve --mdns
Restart=always
RestartSec=3

[Install]
WantedBy=graphical-session.target
EOF

systemctl --user daemon-reload && systemctl --user enable --now opencode-serve.service

SSH#

sudo systemctl enable --now ssh

UFW#

sudo ufw default deny incoming && \
  sudo ufw default allow outgoing && \
  sudo ufw allow OpenSSH && \
  sudo ufw allow kdeconnect && \
  sudo ufw enable

Gaming#

Eden#

Descargá Eden (emulador de Nintendo Switch) como AppImage y manejalo con Gear Lever. Usá el build amd64 PGO para mejor performance.

Sonic Unleashed Recompiled#

Descargá el Flatpak de Unleashed Recompiled e instalalo. Necesitás los archivos del Sonic Unleashed de Xbox 360 (US o EU), el title update, y opcionalmente el DLC (recomendado, incluye iluminación de alta calidad).

wget -O /tmp/UnleashedRecomp-Flatpak.zip \
  https://github.com/hedge-dev/UnleashedRecomp/releases/latest/download/UnleashedRecomp-Flatpak.zip && \
  unzip -o /tmp/UnleashedRecomp-Flatpak.zip -d /tmp/UnleashedRecomp && \
  flatpak install /tmp/UnleashedRecomp/*.flatpak && \
  rm -rf /tmp/UnleashedRecomp /tmp/UnleashedRecomp-Flatpak.zip

Steam#

  • Habilitar Steam Play
  • Opciones de lanzamiento por juego:
gamemoderun %command%
  • Instalar Proton-CachyOS o Proton-GE con ProtonPlus

Half-Life / Portal / Counter-Strike#

-vulkan -novid -fullscreen

Sonic Adventure#

Usar Adventure Mods para configurar mods de Sonic Adventure DX y Sonic Adventure 2 en Linux. Detecta instalaciones de Steam, instala mod managers, mods, dependencias, presets y configuración base.

Descargar el AppImage desde GitHub Releases e instalarlo con Gear Lever.

GTA IV#

Instalar Grand Theft Auto IV: The Complete Edition desde Steam.

Instalar FusionFix: descargar GTAIV.EFLC.FusionFix.zip y extraerlo en la carpeta raíz del juego, donde está el .exe.

Opciones de lanzamiento en Steam:

WINEDLLOVERRIDES="dinput8=n,b" %command%

Git#

git config --global user.name "astrovm"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global rebase.autoStash true
git config --global core.autocrlf input
git config --global core.pager batcat
git config --global fetch.prune true
git config --global rerere.enabled true

ssh-keygen -t ed25519 -C "[email protected]"
eval "$(ssh-agent -s)" && \
  ssh-add ~/.ssh/id_ed25519 && \
  cat ~/.ssh/id_ed25519.pub

Pegar la clave pública en https://github.com/settings/ssh.

Extensiones de Brave#