771 - Adiós a las excusas. Cómo monté mi VS Code en un servidor

771 - Adiós a las excusas. Cómo monté mi VS Code en un servidor

Aprende a montar tu entorno de desarrollo remoto con Code Server y Docker para programar profesionalmente desde cualquier tablet Android o navegador.

1:25
-3:15

Tengo que confesarte que lo de programar en la tablet Android no me está funcionando. Probablemente es porque lo he intentado poco. Pero, como sabes soy bastante cabezón y no me rindo fácilmente. Así, que he decidido darle un enfoque completamente diferente al asunto. En lugar de utilizar Neovim en la tablet, he decidido montar un code-server, para programar en remoto. De esta forma, tengo todo el potencial de un Visual Studio Code, pero directamente en la tablet, sin que sufra lo más mínimo.

Así que para esto, he impolementado una versión personalizada de code-server, con todas las extensiones que suelo utilizar, y la he desplegado en un servidor remoto. Luego, desde la tablet, simplemente accedo a través del navegador web, y listo. Tengo mi entorno de desarrollo completo, con terminal integrada y todo.

Adiós a las excusas. Cómo monté mi VS Code en un servidor

Mi IDE en el navegador

Uno de los inconvenientes de cambiar de entorno de desarrollo es que luego no encuentro las cosas. Esta es la principal razón para sincronizar la configuración, extensiones y preferencias de las herramientas de desarrollo.

Pero todavía hay un paso mas. ¿Y si pudiera tener mi entorno de desarrollo completo, con todas las extensiones y configuraciones, pero accesible desde cualquier dispositivo, incluyendo mi tablet Android?

Pues esto es precisamente lo que he hecho con code-server. He personalizado code-server, y lo he puesto a funcionar en mi VPS. De esta manera con independencia del dispositivo que utilice, siempre tengo acceso a mi entorno de desarrollo completo.

Personalización de code-server

En lugar de utilizar la imagen de code-server por defecto, he optado por una imagen personalizada que incluya las extensiones y configuraciones que usa habitualmente. Esto te ahorra tiempo al no tener que instalar todo desde cero cada vez que levantas el contenedor. Esta es mi personalización de code-server.

# --- ETAPA 1: Construcción de herramientas (Imagen oficial de Rust) ---
FROM rust:slim AS builder

RUN cargo install bat \
                  lsd \
                  starship \
                  sd \
                  cargo-machete \
                  cargo-watch
                  #cargo-update fd-find

# --- ETAPA 2: Imagen de ejecución (Code-Server) ---
FROM lscr.io/linuxserver/code-server:latest

LABEL version="0.1.2"
LABEL author="Lorenzo Carbonell <atareao.es>"

# Variables de entorno para Rust
ENV RUSTUP_HOME=/usr/local/rustup \
    CARGO_HOME=/usr/local/cargo \
    PATH=/usr/local/cargo/bin:$PATH \
    DEBIAN_FRONTEND=noninteractive

# 1. Dependencias de sistema para desarrollo
RUN apt-get update && apt-get install -y \
    build-essential curl git wget neovim ripgrep \
    pkg-config libssl-dev ca-certificates \
    zsh fish ffmpeg 7zip jq cmake gnupg2 pinentry-curses \
    && rm -rf /var/lib/apt/lists/*

# 2. Instalamos Rustup en la imagen final para permitir actualizaciones futuras
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path && \
    rustup component add rust-analyzer \
                         rustfmt \
                         clippy \
                         rust-docs \
                         rust-src \
                         rust-analyzer \
                         llvm-tools-preview \
                         rust-analysis \
                         rustc \
                         cargo \
                         rust-std

# 3. Copiamos las herramientas CLI ya compiladas desde la etapa builder
COPY --from=builder /usr/local/cargo/bin/ /usr/local/bin/

# 4. Permisos totales para el usuario 'abc' en las carpetas de Rust
# Esto es CLAVE para que 'rustup update' funcione desde el contenedor
RUN chmod -R 777 /usr/local/cargo /usr/local/rustup && \
    chsh -s /usr/bin/fish abc && \
    mkdir -p /etc/cont-init.d && \
    echo '#!/with-conten-env sh\n\
# Persistencia de SSH y GPG\n\
for dir in .ssh .gnupg; do\n\
  if [ ! -L "/home/abc/$dir" ]; then\n\
    mkdir -p "/config/$dir"\n\
    chmod 700 "/config/$dir"\n\
    ln -s "/config/$dir" "/home/abc/$dir"\n\
    chown -R abc:abc "/config/$dir" "/home/abc/$dir"\n\
  fi\n\
done\n\
if [ ! -f /config/.config/fish/config.fish ]; then\n\
  mkdir -p /config/.config/fish\n\
  echo "starship init fish | source" > /config/.config/fish/config.fish\n\
  echo "alias ls=\"lsd\"" >> /config/.config/fish/config.fish\n\
  echo "alias l=\"lsd -l\"" >> /config/.config/fish/config.fish\n\
  echo "alias cat=\"bat\"" >> /config/.config/fish/config.fish\n\
  echo "set -gx GPG_TTY (tty)" >> /config/.config/fish/config.fish\n\
  chown -R abc:abc /config/.config\n\
fi' > /etc/cont-init.d/99-rust-setup && chmod +x /etc/cont-init.d/99-rust-setup

WORKDIR /config/workspace

Las características clave de esta imagen personalizada son las siguientes,

  • Arquitectura Multi-etapa: Utiliza una primera etapa de compilación para herramientas Rust y una segunda para la ejecución, optimizando el tamaño final de la imagen.
  • Herramientas CLI Modernas: Incluye utilidades de última generación como bat (sustituto de cat), lsd (sustituto de ls), starship (prompt rápido) y sd (sed moderno).
  • Entorno de Desarrollo Rust Completo: Integra todo lo necesario para programar en Rust, incluyendo rust-analyzer, clippy, rustfmt, y herramientas adicionales como cargo-watch y cargo-machete.
  • Shell Fish por Defecto: Configura fish como la shell de usuario, incluyendo alias preconfigurados y la integración visual con starship.
  • Persistencia de Seguridad: Gestiona automáticamente mediante scripts de inicio los enlaces simbólicos para carpetas críticas como .ssh y .gnupg, asegurando que tus llaves sobrevivan al reinicio del contenedor.
  • Gestión de Permisos: Implementa permisos totales (777) en los directorios de Cargo y Rustup para permitir actualizaciones de herramientas sin conflictos de usuario.
  • Dependencias de Compilación: Incluye librerías esenciales para el desarrollo en Linux como build-essential, libssl-dev, pkg-config y cmake.
  • Automatización con Cont-init: Utiliza el sistema de inicialización de LinuxServer para configurar el entorno de trabajo, los alias y las variables de entorno de forma automática al arrancar.
  • Soporte GPG: Configura correctamente la terminal para el uso de GPG, fundamental para la firma de commits en entornos de desarrollo.

El compose.yml de instalación

Para tenerlo plenamente funcional he introducido algunos puntos que creo te resultarán interesantes. Te dejo el compose.yml que he utilizado para desplegarlo,

2. El Corazón: Instalación de code-server

  • Despliegue con Docker: La forma más limpia (y muy «atareao») de levantarlo.
  • Configuración del config.yaml: Definir la contraseña de acceso y el puerto.
  • Persistencia de datos: Mapeo de volúmenes para que tus proyectos y extensiones no desaparezcan al reiniciar el contenedor.
services:
  custom-code-server:
    image: atareao/custom-code-server
    container_name: custom-code-server
    restart: unless-stopped
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Madrid
      - AUTH=none # Seguridad gestionada por Pocket ID en el borde (Traefik)
      - DOCKER_USER=abc
      - DEFAULT_WORKSPACE=/config/workspace
    volumes:
      - code_server_data:/config # Aquí van las extensiones y configuración de VS Code
      - code_server_workspace:/config/workspace # Aquí es donde desarrollarás tus proyectos
    networks:
      - proxy
    labels:
      traefik.enable: true
      traefik.http.routers.code-server.rule: Host(`${FQDN}`)
      traefik.http.routers.code-server.entrypoints: https
      # Middleware de Pocket ID (Forward Auth)
      traefik.http.routers.code-server.middlewares: oidc-auth@file,my-geoblock@file
      traefik.http.services.code-server.loadbalancer.server.port: 8443

volumes:
  code_server_data:
  code_server_workspace:

networks:
  proxy:
    external: true

Detalles clave a tener en cuenta,

  • Gestión de Usuarios: Configura el contenedor con PUID y PGID 1000 para asegurar que los permisos de los archivos coincidan con tu usuario local.
  • Seguridad Delegada: Desactiva la autenticación nativa de code-server (AUTH=none) porque la seguridad se gestiona externamente.
  • Integración con Traefik: Incluye etiquetas para que Traefik actúe como proxy inverso, gestionando el certificado SSL y el punto de entrada HTTPS.
  • Autenticación OIDC: Implementa un middleware de Pocket ID (oidc-auth@file) para centralizar el acceso mediante Forward Auth.
  • Protección Geográfica: Añade una capa de seguridad adicional mediante el middleware my-geoblock para restringir el acceso por ubicación.
  • Persistencia Doble: Utiliza dos volúmenes distintos; uno para la configuración y extensiones del editor (/config) y otro específico para tus proyectos (/config/workspace).
  • Configuración Regional: Define la zona horaria en Europe/Madrid para que los logs y el sistema coincidan con tu hora local.
  • Red Externa: Se conecta a una red llamada proxy ya existente, permitiendo la comunicación directa con el balanceador de carga.
  • Entorno de Trabajo: Define /config/workspace como el directorio inicial por defecto al abrir el editor.

La configuración de code-server

Algo que pensé que no le dedicaría mucho tiempo es a la configuración de code-server, pero como de costumbre, me equivoqué totalmente. Pero no porque me engañara a mi mismo configurando, sino por necesidad. No es lo mismo trabajar con Visual Studio Code en un equipo de escritorio que con code-server embebido en un navegador web. Así que he tenido que hacer algunos ajustes importantes para optimizar la experiencia.

Aquí tienes la configuración agrupada por bloques lógicos, corrigiendo el error de sintaxis del JSON original (comas sobrantes) y destacando las claves para que todo funcione correctamente en tu entorno de code-server:

  • Comportamiento y Persistencia
    • Autoguardado al perder el foco: files.autoSave: "onFocusChange" asegura que los cambios se guarden al cambiar de pestaña o aplicación.
    • Sincronización automática: sync.autoDownload y sync.autoUpload activados para mantener la configuración al día con la extensión de Settings Sync.
    • Formateo automático: editor.formatOnSave está habilitado globalmente y específico para archivos JSON.
  • Interfaz y Estética (UI)
    • Tema Visual: Utiliza Ayu Dark Bordered.
    • Barra de Actividad: Situada en la parte superior (top) para maximizar el espacio lateral de edición.
    • Minimapa: Desactivado completamente (editor.minimap.enabled: false).
    • Reglas visuales: Líneas verticales en las columnas 80 y 120 con color personalizado.
  • Editor y Tipografía
    • Fuentes: Usa JetBrains Mono y Fira Code con ligaduras activadas.
    • Cursor: Estilo bloque (block) con animación suave y expansión al parpadear.
    • Indentación: Configurada estrictamente a 4 espacios (editor.tabSize: 4) desactivando la detección automática.
    • Guías: Activadas para indentación y pares de corchetes.
  • Configuración Vim (Leader y Esc)
    • Tecla Líder: Definida como la coma (vim.leader: ",").
    • Salida de Modos (jk): Mapeo de jk para actuar como <Esc> en modo inserto, modo visual y modo búsqueda.
    • Portapapeles: vim.useSystemClipboard: true para compartir el portapapeles entre el servidor y tu equipo local.
    • Feedback Visual: Resaltado breve (highlightedyank) al copiar texto.
  • Ajustes Específicos para Funcionamiento en Navegador (code-server)
    • keyboard.dispatch: "keyCode": Fundamental en code-server. Evita que los atajos del navegador (como Ctrl+N o Ctrl+W) entren en conflicto con los de VS Code.
    • vim.insertModeKeyBindings y searchModeKeyBindings: Al mapear jk a Esc, evitas depender de la tecla física Esc, que a menudo el navegador intercepta o ignora.
    • editor.lineNumbers: relative: Esencial para moverte rápido con comandos de Vim (ej. 10j) sin contar líneas manualmente.
    • terminal.integrated.fontFamily: "MesloLGS NF": Necesario para que los iconos de tu shell Fish y el prompt Starship (que configuraste en el Dockerfile) se vean correctamente en la terminal integrada.

El truco de la PWA

Uno de los problemas con los que te me encontré al principio es que, al abrir code-server en el navegador de la tablet, la experiencia no era del todo satisfactoria. Tenía la barra de direcciones y las pestañas del navegador visibles, lo que restaba espacio y hacía que la experiencia se sintiera menos como una aplicación nativa.

La solución a este problema es convertir la web de code-server en una PWA (Progressive Web App). Esto se puede hacer fácilmente en navegadores como Chrome o Edge. Simplemente abres la URL de code-server, y luego seleccionas la opción «Añadir a la pantalla de inicio». Esto crea un acceso directo en tu pantalla de inicio que, al abrirse, lanza code-server en una ventana sin barras de navegador, dándote una experiencia mucho más inmersiva y similar a una aplicación nativa.

De esta forma, desaparece la barra de direcciones y las pestañas, permitiéndote utilizar todo el espacio de la pantalla para tu código. Además, al ser una PWA, puedes incluso configurar atajos de teclado específicos para mejorar tu flujo de trabajo.

Teclado y ¿ratón?

Para mi el tener un teclado físico es imprescindible. No concibo programar sin un teclado físico. Así que este es un punto que ya tengo resuelto. Desde el principio he utilizado un teclado Bluetooth que funciona perfectamente con la tablet.

Respecto al ratón o trackpad, la verdad es que no lo utilizo apenas. Prefiero utilizar atajos de teclado para todo. Y ese es básicamente el objetivo. Añadir los atajos de teclado imprescimindibles para manejar el entorno de desarrollo sin necesidad de tocar la pantalla.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *