Skip to content

📁 File Explorer

Welcome to my file repository! This is where you can find various configuration files, scripts, and resources that I mention in my blog posts. Whether you're looking for my dotfiles, Ansible playbooks, or Docker configurations, you'll find them all organized here.

How to Use

  • Click on a folder (📂) to explore its contents
  • Click on a file name to view its details
  • All files are version controlled in my GitHub repository

📂 File Browser

📂 ansible

Ansible Playbooks and Roles

Playbooks and roles will be added as they are mentioned in blog posts...

📂 docker

Docker Configurations
arr-stack
View docker-compose.yaml
services:
  gluetun:
    image: qmcgaw/gluetun
    container_name: gluetun
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun:/dev/net/tun
    ports:
      - 8080:8080 # qbittorrent web interface
      - 6881:6881 # qbittorrent torrent port
      - 9696:9696 # prowlarr
      - 8989:8989 # sonarr
      - 7878:7878 # radarr
      - 6767:6767 # bazarr
    volumes:
      - /home/serveradmin/docker/gluetun:/gluetun
    environment:
      - VPN_SERVICE_PROVIDER=private internet access
      - OPENVPN_USER=${VPN_USERNAME}
      - OPENVPN_PASSWORD=${VPN_PASSWORD}
      - SERVER_REGIONS=ES Madrid
      - HEALTH_VPN_DURATION_INITIAL=120s
    healthcheck:
      test: ping -c 1 www.google.com || exit 1
      interval: 60s
      timeout: 20s
      retries: 5
    restart: unless-stopped

  deunhealth:
    image: qmcgaw/deunhealth
    container_name: deunhealth
    network_mode: "none"
    environment:
      - LOG_LEVEL=info
      - HEALTH_SERVER_ADDRESS=127.0.0.1:9999
      - TZ=Europe/Amsterdam
    restart: always
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

# Healthcheck was added to work with deunhealth to restart container
# on unhealthy status. labels allows deunhealth to monitor.

  qbittorrent:
    image: lscr.io/linuxserver/qbittorrent:latest
    container_name: qbittorrent
    restart: unless-stopped
    labels:
      - deunhealth.restart.on.unhealthy= "true"
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - WEBUI_PORT=8080
      - TORRENTING_PORT=6881
    volumes:
      - /home/serveradmin/docker/qbittorrent:/config
      - /mnt/data:/data
    network_mode: service:gluetun
    healthcheck:
        test: ping -c 1 www.google.com || exit 1
        interval: 60s
        retries: 3
        start_period: 20s
        timeout: 10s

  prowlarr:
    image: lscr.io/linuxserver/prowlarr:latest
    container_name: prowlarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /home/serveradmin/docker/prowlarr:/config
    restart: unless-stopped
    network_mode: service:gluetun

  sonarr:
    image: lscr.io/linuxserver/sonarr:latest
    container_name: sonarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /home/serveradmin/docker/sonarr:/config
      - /mnt/data:/data
    restart: unless-stopped
    network_mode: service:gluetun

  radarr:
    image: lscr.io/linuxserver/radarr:latest
    container_name: radarr
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Europe/Amsterdam
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /home/serveradmin/docker/radarr:/config
      - /mnt/data:/data
    restart: unless-stopped
    network_mode: service:gluetun

  bazarr:
    image: lscr.io/linuxserver/bazarr:latest
    container_name: bazarr
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /home/serveradmin/docker/bazarr:/config
      - /mnt/data:/data
    restart: unless-stopped
    network_mode: service:gluetun

  jellyseerr:
    image: fallenbagel/jellyseerr:latest
    container_name: jellyseerr
    environment:
      - LOG_LEVEL=debug
      - TZ=Europe/Amsterdam
    ports:
      - 5055:5055
    volumes:
      - /home/serveradmin/docker/jellyseer/config:/app/config
    restart: unless-stopped
beszel
View docker-compose.yaml
services:
  beszel:
    image: henrygd/beszel:latest
    container_name: beszel
    restart: unless-stopped
    ports:
      - 8090:8090
    volumes:
      - /docker/beszel_data:/beszel_data
      - /docker/beszel_socket:/beszel_socket

  beszel-agent:
    image: henrygd/beszel-agent:latest
    container_name: beszel-agent
    restart: unless-stopped
    network_mode: host
    volumes:
      - /docker/beszel_socket:/beszel_socket
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      LISTEN: /beszel_socket/beszel.sock
      # Do not remove quotes around the key
      KEY: 'UPDATE WITH YOUR PUBLIC KEY (copy from "Add system" dialog)'
code-server
View docker-compose.yaml
services:
  code-server:
    image: lscr.io/linuxserver/code-server:latest
    container_name: code-server
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
      - PASSWORD=${CODE_PASSWORD} #optional
      - SUDO_PASSWORD=${CODE_PASSWORD} #optional
    volumes:
      - /docker/code-server/config:/config
    restart: unless-stopped
    networks:
      proxy:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.code-server.entrypoints=http"
      - "traefik.http.routers.code-server.rule=Host(`code-server.xrxapp.org`)"
      - "traefik.http.middlewares.code-server-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.code-server.middlewares=code-server-https-redirect"
      - "traefik.http.routers.code-server-secure.entrypoints=https"
      - "traefik.http.routers.code-server-secure.rule=Host(`code-server.xrxapp.org`)"
      - "traefik.http.routers.code-server-secure.tls=true"
      - "traefik.http.routers.code-server-secure.service=code-server"
      - "traefik.http.services.code-server.loadbalancer.server.port=8443"
      - "traefik.docker.network=proxy"

networks:
  default:
    external: true
    name: proxy
crafty
View docker-compose.yaml
services:
  crafty:
    container_name: crafty_container
    image: registry.gitlab.com/crafty-controller/crafty-4:latest
    restart: always
    environment:
        - TZ=Europe/Amsterdam
    ports:
        - "8443:8443" # HTTPS
        - "8123:8123" # DYNMAP
#        - "19132:19132/udp" # BEDROCK
        - "25500-25600:25500-25600" # MC SERV PORT RANGE
    volumes:
        - /home/serveradmin/docker/crafty-4/backups:/crafty/backups
        - /home/serveradmin/docker/crafty-4/logs:/crafty/logs
        - /home/serveradmin/docker/crafty-4/servers:/crafty/servers
        - /home/serveradmin/docker/crafty-4/config:/crafty/app/config
        - /home/serveradmin/docker/crafty-4/import:/crafty/import
gotify
View docker-compose.yaml
services:
  gotify:
    image: gotify/server
    container_name: gotify
    volumes:
      - /docker/gotify:/app/data
    restart: unless-stopped
#    ports:
#      - 8086:80
    security_opt:
      - no-new-privileges:true
    environment:
      - TZ=Europe/Amsterdam
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gotify.entrypoints=http"
      - "traefik.http.routers.gotify.rule=Host(`gotify.xrxapp.org`)"
      - "traefik.http.middlewares.gotify-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.gotify.middlewares=gotify-https-redirect"
      - "traefik.http.routers.gotify-secure.entrypoints=https"
      - "traefik.http.routers.gotify-secure.rule=Host(`gotify.xrxapp.org`)"
      - "traefik.http.routers.gotify-secure.tls=true"
      - "traefik.http.routers.gotify-secure.service=gotify"
      - "traefik.http.services.gotify.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true
homepage
View docker-compose.yml
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    ports:
      - 3003:3000
    volumes:
      - /docker/homepage/config:/app/config # Make sure your local config directory exists
      - /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations
    environment:
      HOMEPAGE_ALLOWED_HOSTS: homepage.xrx-tech.org # required
it-tools
View docker-compose.yaml
services:
    it-tools:
        image: 'corentinth/it-tools:latest'
#        ports:
#            - '8083:80'
        restart: unless-stopped
        container_name: it-tools
        networks:
          - proxy
        labels:
          - "traefik.enable=true"
          - "traefik.docker.network=proxy"
          - "traefik.http.routers.it-tools.entrypoints=http"
          - "traefik.http.routers.it-tools.rule=Host(`it-tools.xrxapp.org`)"
          - "traefik.http.middlewares.it-tools-https-redirect.redirectscheme.scheme=https"
          - "traefik.http.routers.it-tools.middlewares=it-tools-https-redirect"
          - "traefik.http.routers.it-tools-secure.entrypoints=https"
          - "traefik.http.routers.it-tools-secure.rule=Host(`it-tools.xrxapp.org`)"
          - "traefik.http.routers.it-tools-secure.tls=true"
          - "traefik.http.routers.it-tools-secure.tls.certresolver=cloudflare"
          - "traefik.http.routers.it-tools-secure.service=it-tools"
          - "traefik.http.services.it-tools.loadbalancer.server.port=80"

networks:
  proxy:
    external: true
libreoffice
View docker-compose.yaml
services:
  libreoffice:
    image: lscr.io/linuxserver/libreoffice:latest
    container_name: libreoffice
    security_opt:
      - seccomp:unconfined #optional
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    volumes:
      - /docker/libreoffice/config:/config
      - /docker/libreoffice/documents:/data/nextcloud/libreoffice
    ports:
      - 3003:3000
      - 3004:3001
    restart: unless-stopped
netbootxyz
View docker-compose.yaml
services:
  netbootxyz:
    image: ghcr.io/netbootxyz/netbootxyz
    container_name: netbootxyz
    environment:
      - TZ=Europe/Amsterdam
    volumes:
      - /home/serveradmin/docker/netbootxyz/config:/config # optional
      - /mnt/data/isos:/assets # optional

    ports:
      - 69:69/udp
      - 3000:3000
    restart: unless-stopped
npm
View docker-compose.yaml
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm
    restart: unless-stopped
    ports:
      # These ports are in format <host-port>:<container-port>
      - '80:80' # Public HTTP Port
      - '443:443' # Public HTTPS Port
      - '81:81' # Admin Web Port
      # Add any other Stream port you want to expose
      # - '21:21' # FTP

    environment:
      # Uncomment this if you want to change the location of
      # the SQLite DB file within the container
      # DB_SQLITE_FILE: "/data/database.sqlite"

      # Uncomment this if IPv6 is not enabled on your host
       DISABLE_IPV6: 'true'
       TZ: Europe/Amsterdam

    volumes:
      - /home/serveradmin/docker/npm/data:/data
      - /home/serveradmin/docker/npm/letsencrypt:/etc/letsencrypt

    networks:
      - proxy

    healthcheck:
      test: ["CMD", "/usr/bin/check-health"]
      interval: 10s
      timeout: 3s

networks:
  proxy:
    external: true
ollama
View docker-compose.yaml
services:
  ollama:
    # Uncomment below for GPU support
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities:
                - gpu
    volumes:
      - /home/serveradmin/docker/ollama:/root/.ollama
    # Uncomment below to expose Ollama API outside the container stack
    ports:
      - 11434:11434
    container_name: ollama
    environment:
      - TZ=Europe/Amsterdam
    pull_policy: always
    tty: true
    restart: unless-stopped
    image: ollama/ollama:latest
    networks:
      - ollama-net

  openwebui:
    image: ghcr.io/open-webui/open-webui:main
    container_name: openwebui
    ports:
      - "3000:8080"
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
      - TZ=Europe/Amsterdam
    depends_on:
      - ollama
    restart: unless-stopped
    networks:
      - ollama-net

networks:
  ollama-net:
    external: true
paperless
View docker-compose.yaml
services:
  broker:
    image: docker.io/library/redis:7
    restart: unless-stopped
    volumes:
      - /docker/paperless-ngx/redis-data:/data

  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - broker
    ports:
      - "8001:8000"
    volumes:
      - /docker/paperless-ngx/data:/usr/src/paperless/data
      - /docker/paperless-ngx/media:/usr/src/paperless/media
      - /docker/paperless-ngx/export:/usr/src/paperless/export
      - /docker/paperless-ngx/consume:/usr/src/paperless/consume
    env_file: .env
    environment:
      PAPERLESS_REDIS: redis://broker:6379
restic
View docker-compose.yaml
services:
  backup:
    image: mazzolino/restic
    container_name: restic
    hostname: servarr
    restart: unless-stopped
    environment:
      RUN_ON_STARTUP: "true" #change as you wish
      BACKUP_CRON: "0 */12 * * *" #this is twice daily, i.e., every 12 hours
      RESTIC_REPOSITORY: /restic
      RESTIC_PASSWORD: admin
      RESTIC_BACKUP_SOURCES: /mnt/volumes
      RESTIC_COMPRESSION: auto 
      RESTIC_BACKUP_ARGS: >-
        --tag restic-proxmox #add tags, whatever you need to mark backups
        --verbose
      RESTIC_FORGET_ARGS: >- #change as required
        --keep-last 10
        --keep-daily 7
        --keep-weekly 5
        --keep-monthly 12
      TZ: Europe/Amsterdam
    volumes:
      - /data/backups/docker-backup:/restic #change the left hand side to where you want to store the backups. As you can see I'm storing it on my NAS that is mounted to the host /home/truenas
      - /data/backups/docker-backup/tmp-for-restore:/tmp-for-restore #USE THIS FOLDER FOR RESTORE - CAN VIEW EACH CONTAINER
      - /docker:/mnt/volumes:ro
    security_opt:
      - no-new-privileges:true

  prune:
    image: mazzolino/restic
    restart: unless-stopped
    container_name: restic-prune
    hostname: servarr
    environment:
      RUN_ON_STARTUP: "true"
      PRUNE_CRON: "0 0 4 * * *"
      RESTIC_REPOSITORY: /restic
      RESTIC_PASSWORD: admin
      TZ: Europe/Amsterdam
    security_opt:
      - no-new-privileges:true

  check:
    image: mazzolino/restic
    restart: unless-stopped
    container_name: restic-check
    hostname: servarr
    environment:
      RUN_ON_STARTUP: "false"
      CHECK_CRON: "0 15 5 * * *"
      RESTIC_CHECK_ARGS: >-
        --read-data-subset=10%
      RESTIC_REPOSITORY: /restic
      RESTIC_PASSWORD: admin
      TZ: Europe/Amsterdam
    security_opt:
      - no-new-privileges:true
searxng
View docker-compose.yaml
services:
    searxng:
        image: searxng/searxng:latest
        restart: unless-stopped
        ports:
          - '${PORT}:8080'
        environment:
          - SEARXNG_BASE_URL=https://${SEARXNG_HOSTNAME}/
          - UWSGI_WORKERS=${SEARXNG_UWSGI_WORKERS:-4}
          - UWSGI_THREADS=${SEARXNG_UWSGI_THREADS:-4}        
        volumes:
            - '/docker/searxng/searxng-data:/etc/searxng:rw'
        cap_drop:
          - ALL
        cap_add:
          - CHOWN
          - SETGID
          - SETUID
        logging:
          driver: "json-file"
          options:
            max-size: "1m"
            max-file: "1"
vaultwarden
View docker-compose.yaml
services:
  vaultwarden:
    container_name: vaultwarden
    environment:
      - TZ=Europe/Amsterdam
    image: vaultwarden/server:latest
    volumes:
      - /docker/vaultwarden/:/data/
    restart: unless-stopped
#    ports:
#      - 8085:80
    security_opt:
      - no-new-privileges:true
    networks:
      proxy:
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.entrypoints=http"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.yourdomain.com`)"
      - "traefik.http.middlewares.vaultwarden-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.vaultwarden.middlewares=vaultwarden-https-redirect"
      - "traefik.http.routers.vaultwarden-secure.entrypoints=https"
      - "traefik.http.routers.vaultwarden-secure.rule=Host(`vaultwarden.yourdomain.com`)"
      - "traefik.http.routers.vaultwarden-secure.tls=true"
      - "traefik.http.routers.vaultwarden-secure.service=vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

📂 dotfiles

Personal Dotfiles
zshrc
View zshrc
# ---------------------------------------------------------------------------------
#| __  __ ___ __  __       ____ ___  _  _         ___   ___   _  _  ___  ___   ___ |
#| \ \/ /| _ \\ \/ /      |_  // __|| || |       / __| / _ \ | \| || __||_ _| / __||
#|  >  < |   / >  <        / / \__ \| __ |      | (__ | (_) || .  || _|  | | | (_ ||
#| /_/\_\|_|_\/_/\_\      /___||___/|_||_|       \___| \___/ |_|\_||_|  |___| \___||
#|_________________________________________________________________________________|

# Paths
export PATH="/usr/local/sbin:$PATH"
export PATH="/usr/local/bin:$PATH"
export PATH=$PATH:/usr/local/nvim/bin
export PATH="$HOME/.local/bin:$PATH"
export NVM_DIR="$HOME/.nvm"
# Inits
eval "$(starship init zsh)" # starship
eval "$(zoxide init zsh)" # zoxide
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
# Aliases
alias superls='fzf --preview="bat --color=always {}"'
alias cd="z"
alias ls="eza --icons=always"
alias reload-zsh="source ~/.zshrc"
# Auto attach to a specific tmux session (e.g., "dev") if not already in tmux
if [ -z "$TMUX" ]; then
  tmux attach-session -t xrx-default || tmux new-session -s xrx-default
fi
# FZF fuzzy history search bound to Ctrl+H
fzf-history-widget() {
  local selected
  selected=$(print -rl -- ${(o)history[@]} | fzf --tac --no-sort) || return
  LBUFFER+=$selected
  zle reset-prompt
}
zle -N fzf-history-widget
bindkey '^H' fzf-history-widget
gitconfig
View gitconfig
# This is Git's per-user configuration file.
[user]
# Please adapt and uncomment the following lines:
    name = wanchufley
    email = xrx@xrx-tech.org
[credential]
    helper = store
bashrc
View bashrc
# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

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

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
#[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color|*-256color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    # We have color support; assume it's compliant with Ecma-48
    # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
    # a case would tend to support setf rather than setaf.)
    color_prompt=yes
    else
    color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    #alias grep='grep --color=auto'
    #alias fgrep='fgrep --color=auto'
    #alias egrep='egrep --color=auto'
fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
#alias ll='ls -l'
#alias la='ls -A'
#alias l='ls -CF'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

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

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
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

# ------- END OF DEFAULT CONF -------

# xrx's bashrc

# Paths
export PATH=$HOME/go/bin:$PATH # goose
export PATH="$PATH:/opt/nvim-linux-x86_64/bin" # neovim
export PATH="/snap/bin:$PATH" # snap
export PATH="/home/xrx/.config/herd-lite/bin:$PATH"
export PHP_INI_SCAN_DIR="/home/xrx/.config/herd-lite/bin:$PHP_INI_SCAN_DIR"
# Aliases
alias superls='fzf --preview="batcat --color=always {}"' # superls alias
alias cd="z" # zoxide alias
alias ls="eza --icons=always" # eza alias

# Inits
eval "$(zoxide init bash)" # zoxide
eval "$(starship init bash)" # starship
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" # homebrew

# Customs
# Auto attach to a specific tmux session (e.g., "dev") if not already in tmux
if [ -z "$TMUX" ]; then
  tmux attach-session -t xrx-default || tmux new-session -s xrx-default
fi
# FZF fuzzy search through command history using Ctrl+H
__fzf_history_search() {
  local selected
  selected=$(history | tac | fzf +s --tac --no-sort) || return
  READLINE_LINE="${selected#* }"
  READLINE_POINT=${#READLINE_LINE}
}
bind -x '"\C-h": __fzf_history_search'
bootstrap-scripts
View bootstrap.sh
#!/bin/bash

set -e  # Exit on error

echo "Creating symlinks with backups..."

# Helper function to back up and symlink
link_with_backup() {
    local source="$1"
    local target="$2"
    local backup="${target}.bak"

    # Backup if target exists and is not a symlink
    if [ -e "$target" ] && [ ! -L "$target" ]; then
        echo "Backing up $target to $backup"
        mv "$target" "$backup"
    fi

    echo "Linking $target -> $source"
    ln -sf "$source" "$target"
}

# Base path to dotfiles
DOTFILES_DIR=~/repositories/homelab/dotfiles

# Symlink for .bashrc
link_with_backup "$DOTFILES_DIR/bashrc" ~/.bashrc

# Ensure ~/.config/tmux exists
mkdir -p ~/.config/tmux

# Symlink for tmux.conf
link_with_backup "$DOTFILES_DIR/.config/tmux/tmux.conf" ~/.config/tmux/tmux.conf

# Symlink for .gitconfig
link_with_backup "$DOTFILES_DIR/gitconfig" ~/.gitconfig

# Symlink starship.toml
link_with_backup "$DOTFILES_DIR/.config/starship.toml" ~/.config/starship.toml

echo "All symlinks created and backups made (if needed)!"
View install-packages.sh
#!/bin/bash
set -e

PACKAGE_FILE="packages.txt"

if [ ! -f "$PACKAGE_FILE" ]; then
    echo "Package list not found!"
    exit 1
fi

echo "Updating package lists..."
sudo apt update

echo "Installing packages from $PACKAGE_FILE..."
xargs -a "$PACKAGE_FILE" sudo apt install -y
View packages.txt
git
tmux
curl
bat
fzf
btop
ansible
.config
starship.toml
View starship.toml
format = """
[░▒▓](#7aa2f7)\
[🐧](bg:#7aa2f7 fg:#1a1b26)\
[](bg:#9d7cd8 fg:#7aa2f7)\
$directory\
[](fg:#9d7cd8 bg:#3b4261)\
$git_branch\
$git_status\
[](fg:#3b4261 bg:#2a2e3f)\
$nodejs\
$rust\
$golang\
$php\
[](fg:#2a2e3f bg:#1a1b26)\
$time\
[ ](fg:#1a1b26)\
\n$character"""

[directory]
style = "fg:#c0caf5 bg:#9d7cd8"
format = "[ $path ]($style)"
truncation_length = 3
truncation_symbol = "…/"

[directory.substitutions]
"Documents" = "󰈙 "
"Downloads" = " "
"Music" = " "
"Pictures" = " "

[git_branch]
symbol = ""
style = "bg:#3b4261"
format = '[[ $symbol $branch ](fg:#7aa2f7 bg:#3b4261)]($style)'

[git_status]
style = "bg:#3b4261"
format = '[[($all_status$ahead_behind )](fg:#7aa2f7 bg:#3b4261)]($style)'

[nodejs]
symbol = ""
style = "bg:#2a2e3f"
format = '[[ $symbol ($version) ](fg:#7aa2f7 bg:#2a2e3f)]($style)'

[rust]
symbol = ""
style = "bg:#2a2e3f"
format = '[[ $symbol ($version) ](fg:#7aa2f7 bg:#2a2e3f)]($style)'

[golang]
symbol = ""
style = "bg:#2a2e3f"
format = '[[ $symbol ($version) ](fg:#7aa2f7 bg:#2a2e3f)]($style)'

[php]
symbol = ""
style = "bg:#2a2e3f"
format = '[[ $symbol ($version) ](fg:#7aa2f7 bg:#2a2e3f)]($style)'

[time]
disabled = false
time_format = "%R"
style = "bg:#1a1b26"
format = '[[  $time ](fg:#9ece6a bg:#1a1b26)]($style)'
tmux
View tmux.conf
# Set right colors
set-option -sa terminal-overrides ",xterm*:Tc"

# Set mouse mode
set -g mouse on

# Set vi mode
set-window-option -g mode-keys vi

# Vim style pane selection
bind h select-pane -L
bind j select-pane -D 
bind k select-pane -U
bind l select-pane -R

# Shift Alt vim keys to switch windows
bind -n M-H previous-window
bind -n M-L next-window

# Start windows and panes at 1, not 0
set -g base-index 1
set -g pane-base-index 1
set-window-option -g pane-base-index 1
set-option -g renumber-windows on

# Plugins' arguments
set -g @clipboard "wl-copy"
set -g @resurrect-auto-restore 'on'
set -g automatic-rename off
set -g allow-rename off

# Plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-sensible'
set -g @plugin 'tmux-plugins/tmux-yank'
set -g @plugin 'tmux-plugins/tmux-resurrect'
set -g @plugin 'fabioluciano/tmux-tokyo-night'

# Sends yank copy buffer to default clipboard
bind-key -T copy-mode-vi y send -X copy-pipe-and-cancel "wl-copy"


run '~/.tmux/plugins/tpm/tpm'
aerospace
View aerospace.toml
# Place a copy of this config to ~/.aerospace.toml
# After that, you can edit ~/.aerospace.toml to your liking

# You can use it to add commands that run after login to macOS user session.
# 'start-at-login' needs to be 'true' for 'after-login-command' to work
# Available commands: https://nikitabobko.github.io/AeroSpace/commands
after-login-command = []

# You can use it to add commands that run after AeroSpace startup.
# 'after-startup-command' is run after 'after-login-command'
# Available commands : https://nikitabobko.github.io/AeroSpace/commands
after-startup-command = []

# Start AeroSpace at login
start-at-login = true 

# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization
enable-normalization-flatten-containers = true
enable-normalization-opposite-orientation-for-nested-containers = true

# See: https://nikitabobko.github.io/AeroSpace/guide#layouts
# The 'accordion-padding' specifies the size of accordion padding
# You can set 0 to disable the padding feature
accordion-padding = 30

# Possible values: tiles|accordion
default-root-container-layout = 'tiles'

# Possible values: horizontal|vertical|auto
# 'auto' means: wide monitor (anything wider than high) gets horizontal orientation,
#               tall monitor (anything higher than wide) gets vertical orientation
default-root-container-orientation = 'auto'

# Mouse follows focus when focused monitor changes
# Drop it from your config, if you don't like this behavior
# See https://nikitabobko.github.io/AeroSpace/guide#on-focus-changed-callbacks
# See https://nikitabobko.github.io/AeroSpace/commands#move-mouse
# Fallback value (if you omit the key): on-focused-monitor-changed = []
on-focused-monitor-changed = ['move-mouse monitor-lazy-center']

# You can effectively turn off macOS "Hide application" (cmd-h) feature by toggling this flag
# Useful if you don't use this macOS feature, but accidentally hit cmd-h or cmd-alt-h key
# Also see: https://nikitabobko.github.io/AeroSpace/goodies#disable-hide-app
automatically-unhide-macos-hidden-apps = false

# Possible values: (qwerty|dvorak|colemak)
# See https://nikitabobko.github.io/AeroSpace/guide#key-mapping
[key-mapping]
    preset = 'qwerty'

# Gaps between windows (inner-*) and between monitor edges (outer-*).
# Possible values:
# - Constant:     gaps.outer.top = 8
# - Per monitor:  gaps.outer.top = [{ monitor.main = 16 }, { monitor."some-pattern" = 32 }, 24]
#                 In this example, 24 is a default value when there is no match.
#                 Monitor pattern is the same as for 'workspace-to-monitor-force-assignment'.
#                 See:
#                 https://nikitabobko.github.io/AeroSpace/guide#assign-workspaces-to-monitors
[gaps]
    inner.horizontal = 8 
    inner.vertical =   8
    outer.left =       8
    outer.bottom =     8
    outer.top =        8 
    outer.right =      8

# 'main' binding mode declaration
# See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes
# 'main' binding mode must be always presented
# Fallback value (if you omit the key): mode.main.binding = {}
[mode.main.binding]

    # All possible keys:
    # - Letters.        a, b, c, ..., z
    # - Numbers.        0, 1, 2, ..., 9
    # - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9
    # - F-keys.         f1, f2, ..., f20
    # - Special keys.   minus, equal, period, comma, slash, backslash, quote, semicolon,
    #                   backtick, leftSquareBracket, rightSquareBracket, space, enter, esc,
    #                   backspace, tab, pageUp, pageDown, home, end, forwardDelete,
    #                   sectionSign (ISO keyboards only, european keyboards only)
    # - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual,
    #                   keypadMinus, keypadMultiply, keypadPlus
    # - Arrows.         left, down, up, right

    # All possible modifiers: cmd, alt, ctrl, shift

    # All possible commands: https://nikitabobko.github.io/AeroSpace/commands

    # See: https://nikitabobko.github.io/AeroSpace/commands#exec-and-forget
    # You can uncomment the following lines to open up terminal with alt + enter shortcut
    # (like in i3)
    # alt-enter = '''exec-and-forget osascript -e '
    # tell application "Terminal"
    #     do script
    #     activate
    # end tell'
    # '''

    # See: https://nikitabobko.github.io/AeroSpace/commands#layout
    alt-slash = 'layout tiles horizontal vertical'
    alt-comma = 'layout accordion horizontal vertical'

    # See: https://nikitabobko.github.io/AeroSpace/commands#focus
    alt-h = 'focus left'
    alt-j = 'focus down'
    alt-k = 'focus up'
    alt-l = 'focus right'

    # See: https://nikitabobko.github.io/AeroSpace/commands#move
    alt-shift-h = 'move left'
    alt-shift-j = 'move down'
    alt-shift-k = 'move up'
    alt-shift-l = 'move right'

    # See: https://nikitabobko.github.io/AeroSpace/commands#resize
    alt-minus = 'resize smart -50'
    alt-equal = 'resize smart +50'

    # See: https://nikitabobko.github.io/AeroSpace/commands#workspace
    alt-1 = 'workspace 1'
    alt-2 = 'workspace 2'
    alt-3 = 'workspace 3'
    alt-4 = 'workspace 4'
    alt-5 = 'workspace 5'
    alt-6 = 'workspace 6'
    alt-7 = 'workspace 7'
    alt-8 = 'workspace 8'
    alt-9 = 'workspace 9'
    alt-b = 'workspace B'
    alt-t = 'workspace T'
    alt-d = 'workspace D'
    alt-w = 'workspace W'
    alt-m = 'workspace M'

    # See: https://nikitabobko.github.io/AeroSpace/commands#move-node-to-workspace
    alt-shift-1 = 'move-node-to-workspace 1'
    alt-shift-2 = 'move-node-to-workspace 2'
    alt-shift-3 = 'move-node-to-workspace 3'
    alt-shift-4 = 'move-node-to-workspace 4'
    alt-shift-5 = 'move-node-to-workspace 5'
    alt-shift-6 = 'move-node-to-workspace 6'
    alt-shift-7 = 'move-node-to-workspace 7'
    alt-shift-8 = 'move-node-to-workspace 8'
    alt-shift-9 = 'move-node-to-workspace 9'
    alt-shift-b = 'move-node-to-workspace B'
    alt-shift-t = 'move-node-to-workspace T'
    alt-shift-d = 'move-node-to-workspace D'
    alt-shift-w = 'move-node-to-workspace W'
    alt-shift-m = 'move-node-to-workspace M'

    # See: https://nikitabobko.github.io/AeroSpace/commands#workspace-back-and-forth
    alt-tab = 'workspace-back-and-forth'
    # See: https://nikitabobko.github.io/AeroSpace/commands#move-workspace-to-monitor
    alt-shift-tab = 'move-workspace-to-monitor --wrap-around next'

    # See: https://nikitabobko.github.io/AeroSpace/commands#mode
    alt-shift-semicolon = 'mode service'

# 'service' binding mode declaration.
# See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes
[mode.service.binding]
    esc = ['reload-config', 'mode main']
    r = ['flatten-workspace-tree', 'mode main'] # reset layout
    f = ['layout floating tiling', 'mode main'] # Toggle between floating and tiling layout
    backspace = ['close-all-windows-but-current', 'mode main']

    # sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2
    #s = ['layout sticky tiling', 'mode main']

    alt-shift-h = ['join-with left', 'mode main']
    alt-shift-j = ['join-with down', 'mode main']
    alt-shift-k = ['join-with up', 'mode main']
    alt-shift-l = ['join-with right', 'mode main']

    down = 'volume down'
    up = 'volume up'
    shift-down = ['volume set 0', 'mode main']

# Automatically move certain windows to its respective workspace.
# Terminal
[[on-window-detected]]
if.app-id = 'com.mitchellh.ghostty'
run = "move-node-to-workspace T"
# Browser
[[on-window-detected]]
if.app-id = 'app.zen-browser.zen'
run = "move-node-to-workspace B"
# Discord 
[[on-window-detected]]
if.app-id = 'com.hnc.Discord'
run = "move-node-to-workspace D"
# Whatsapp 
[[on-window-detected]]
if.app-id = 'net.whatsapp.WhatsApp'
run = "move-node-to-workspace W"
# Mail 
[[on-window-detected]]
if.app-id = 'com.apple.mail'
run = "move-node-to-workspace M"

💡 Contributing

If you'd like to suggest improvements or report issues with any of the files, please visit the GitHub repository and create an issue or pull request.

Found something interesting?

If you find any of these configurations useful, feel free to use them in your own setup! Just remember to review and adjust them to your needs before implementing.