Blog 

Estás viendo posts de #tecnología, ver todos

Dozzle: logs de Docker para vagos

Publicado el 20 de Abril de 2026 en  

Tengo docenas de contenedores de Docker corriendo en mi laboratorio hogareño, y docker compose logs no es la manera más feliz de debuguear un problema.

Durante meses estuve usando una estructura bizantina de volúmenes montados en las rutas donde cada contenedor escribe logs, pero me volvía loco con los permisos: cada aplicación de cada stack reacciona de manera distinta y contradictoria, desde "decime dónde te logueo, jefe, solamente fijate que el directorio exista porque no me quiero tomar la atribución de crearlo" hasta "si el archivo no tiene exactamente los permisos que espero, me hago pis y caca encima y me cierro".

En fin, evidentemente no soy el único con estos problemas, porque Dozzle parece hecho a medida. Es un servicio que lee el socket de Docker, captura los eventos al vuelo y los muestra en tiempo real en un panel web. Está diseñado para ser fácil de implementar. Esta es mi configuración local:

services:
  dozzle:
    container_name: dozzle
    image: amir20/dozzle:latest
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./dozzle/data:/data
    ports:
      - 55555:8080

Y badabing, badabang, tengo logging de todo lo que pasa en mis contenedores. Gracias Amir, te quiero mucho.


Aplicaciones en Cloudflare que responden en todos los subdominios

Publicado el 4 de Abril de 2026 en  

La plataforma de aplicaciones de Cloudflare tiene una limitación bien conocida: no se puede usar patrones con comodines en la ruta (por ejemplo, *.misitio.com). Si uno necesita alojar una aplicación en varios subdominios, tiene que agregar cada uno como una ruta distinta. Esto tiene dos desventajas. La primera: qué paja. La segunda: todos los subdominios existentes son visibles en los registros DNS.

Todo el mundo resuelve esto con redirects, pero yo no quería redirigir al usuario a otra URL, sino servir contenido en cualquier subdominio.

Al principio pensé que la solución era simple: si internamente dirigía todas las peticiones a un mismo lugar, podía alojar la aplicación ahí y responder según la url que figurara en la request. Pero historia larga hecha corta, no se puede: el router de Cloudflare rebota cualquier petición que no haya llegado al subdominio específico donde corre la aplicación.

Estuve un par de tardes peleando con este problema hasta que encontré la solución. Un worker sí puede usar patrones con comodines en la ruta, y además tiene la capacidad de reescribir la petición y devolvérsela al router de Cloudflare para que siga procesándola.

Con Wrangler instalado, usé la siguiente configuración en wrangler.jsonc:

{
  "name": "misitio-worker",
  "main": "index.js",
  "compatibility_date": "2026-01-14",
  "route": {
    "pattern": "*.misitio.com/*",
    "zone_name": "misitio.com"
  }
}

Esta configuración monta un worker que responde en todos los subdominios. El código de index.js es:

function stripSubdomain(hostname) {
    const parts = hostname.split('.');
    if (parts.length <= 2) return hostname;
    return parts.slice(-2).join('.');
}

export default {
    fetch: async (req) => {
        const originalUrl = new URL(req.url);
        const modifiedUrl = new URL(req.url);
        const rootDomain = stripSubdomain(originalUrl.hostname);

        modifiedUrl.hostname = rootDomain;

        const newReq = new Request(modifiedUrl, req);
        newReq.headers.set('X-Original-Hostname', originalUrl.hostname);

        return fetch(newReq, { cf: { resolveOverride: rootDomain } });
    }
};

Esto intercepta la request, la modifica apuntándola a la ruta raíz, y graba la ruta original en la cabecera X-Original-Hostname para poder usarla más adelante.

Una vez que este worker está deployado en un dominio, solamente falta configurar el DNS. Hay que indicarle al navegador que el dominio acepta peticiones a cualquier subdominio. Hay varias formas de hacer esto, pero en Cloudflare la más simple es con un registro AAAA. En Name ponemos * y en Content ponemos 100::.

Hecho esto, cuando llega una petición a algo.misitio.com, el worker le sobreescribe la ruta a misitio.com, y ahí alguna aplicación que tengamos montada (un túnel, por ejemplo) la procesa, aprovechando X-Original-Hostname.

Por ejemplo, mi túnel Zero Trust apunta a un proxy Traefik que captura ciertas peticiones así:

- "traefik.http.routers.misitio.rule=HeaderRegexp(`X-Original-Hostname`, `^misubdominio\\..+$`)"

Usando esta lógica se puede modificar completamente el flujo interno de peticiones. La única desventaja es el límite de cien mil ejecuciones de worker gratuitas por día, pero para el uso que le estoy dando, me sobra.


Servidores web instantáneos con Docker

Publicado el 4 de Abril de 2026 en  

A veces necesitamos montar un servidor web de prueba y no tenemos ganas de pensar. Para estas situaciones podemos usar aplicaciones de Docker que se definen en un par de líneas y salen andando.

Opción 1: echo server

La opción más ligera es un echo server programado en Go que devuelve una respuesta HTTP 200 con un texto arbitrario.

En un docker-compose.yml ponemos:

services:
  web:
    image: hashicorp/http-echo
    command: ["-text", "Hola gato"]
    restart: unless-stopped
    ports:
      - 80:5678

Corremos docker compose up -d y tenemos servidor.

Opción 2: PHP + Apache

Otra opción es usar la imagen oficial de Docker de PHP que viene con un servidor Apache:

services:
  web:
    image: php:8.5-apache
    restart: unless-stopped
    volumes:
      - ./www:/var/www/html
    ports:
      - 80:80

Por defecto Apache viene con mod_rewrite desactivado, pero podemos activarlo durante la creación de la imagen:

services:
  web:
    build:
      dockerfile_inline: |
        FROM php:8.5-apache
        RUN a2enmod rewrite
    restart: unless-stopped
    volumes:
      - ./www:/var/www/html
    ports:
      - 80:80

Opción 3: PHP + Apache en Alpine

También podemos usar una imagen no oficial basada en Alpine que viene con varios mods activados, incluyendo mod_rewrite y SSL:

services:
  web:
    image: eriksoderblom/alpine-apache-php:latest
    restart: unless-stopped
    volumes:
      - ./www:/htdocs
    ports:
      - 80:80
      - 443:443

En el directorio www ponemos el sitio, y listo el pollo.

Personalmente prefiero usar aplicaciones de Docker Compose porque son expresivas y es muy fácil manipular redes y volúmenes, pero también se puede configurar con un solo comando:

docker run --detach \
    --name web \
    --publish 80:80 \
    --restart unless-stopped \
    --volume ./www:/htdocs \
    eriksoderblom/alpine-apache-php:latest

Colorear tokens específicos en VS Code, Cursor y demases

Publicado el 30 de Marzo de 2026 en  

VS Code y sus forks permiten colorear tokens especificos a partir de sus TextMate scopes. Para saber qué scopes tiene un token, ponemos el cursor sobre él, abrimos la paleta de comandos y buscamos "Developer: Inspect Editor Tokens And Scopes". El diálogo muestra todos los scopes aplicables.


El tag de apertura <?php tiene el scope punctuation.section.embedded.begin.php

Copypasteamos el scope que nos interesa y agregamos un bloque como este a nuestro settings.json:

"editor.tokenColorCustomizations": {
    "textMateRules": [
        {
            "scope": [
                "punctuation.section.embedded.begin.php",
                "punctuation.section.embedded.end.php",
            ],
            "settings": {
                "foreground": "#ff0",
            }
        }
    ]
},

Guardamos los cambios, y listo.

Aparentemente el coloreado semántico tiene precedencia sobre el coloreado por token, así que puede que tengas que desactivarlo buscando "semantic highlighting" en la configuración de VS Code.


Switch Angel cocinando Strudel

Publicado el 29 de Marzo de 2026 en    

La reina del algorave livecodeando EDM en Strudel como quien elige un programa de lavado.

Tenés mi atención, sis.


Tema nivel celu: Keygen Church - Bullug Gegbug Ibgabiug Gixcure Dagabciea Fuic

Publicado el 29 de Marzo de 2026 en        

Ayer hablé de King Stephen, y no puedo pensar en él sin pensar también en Master Boot Record.

Master Boot Record es un productor italiano que hace chiptune, dark synth, metal sinfónico e industrial. Tiene un proyecto secundario que se llama Keygen Church y suena como música de iglesia, si la iglesia estuviera dedicada al Omnissiah.


Así recuerdo las clases de Catequesis

La palabra "keygen" me despierta recuerdos muy vívidos de madrugadas de verano tomando Ades de durazno y comiendo pizza fría del día anterior, perdido en páginas con fondo negro y letras verdes mientras el resto del mundo dormía. Era feliz y no lo sabía.

Pero tengo una relación muy rara con la retroinformática (a diferencia de mis demás relaciones, que son todas normales y neurotípicas). Siento un cariño enorme por la computación de los 90: PC Users, revistas gallegas con CDs de shareware, Astalavista, Geocities, IRC, Looksharp, SuperKoD, Netbus, SubSeven Bonus... Son literalmente mi infancia. Pero ese afecto es por las lindas experiencias compartidas, y daría cualquier cosa para NO volver a eso.

Este mismo sitio web es un reflejo de ese sentimiento: tiene la estética de mi primera página de Geocities de 1998, pero corre en un Docker con Nginx y tiene diseño responsive. Es un homenaje a mis raíces, no una declaración de que "todo tiempo pasado fue mejor".

No fue un carajo mejor: Windows 95 era una porquería inestable, Linux era inescrutable, navegar con un modem de 56k (que levantaba 33,6k en un buen día) ocupando la línea telefónica y pagando llamada local era un bajón, mi Pentium 75 Mhz con 8 MB de RAM y 812 MB de disco era un pisapapeles que tardaba 5 minutos de reloj en abrir Paint Shop Pro shareware... Se me revuelve el estómago.

Últimamente pareciera que en el Siglo XXI lo único que vende es la nostalgia por el Siglo XX: cuando el futuro se ve tan oscuro, la única que queda es mirar al pasado.


Gracias, Hollywood, es exactamente lo que quería

Pero lo que siento no se trata de nostalgia. Recuerdo con cariño los 90 porque no tenía responsabilidades ni preocupaciones, así que podía quedarme toda la noche en IRC chateando (o en IRCWar) con mis amigos, armando pavadas en Visual Basic 6 o Front Page 98, leyendo tequistés sobre phreaking que habían circulado por BBS durante 20 años antes de terminar en una página de Angelfire, o grindeando madera para la barca en el Argentum mientras escuchaba Theatre of Tragedy. Pero hoy también disfruto todas esas cosas: mi relación con la tecnología no cambió. En ese sentido, el chiptune, el retrofuturismo y la informática vintage son una celebración del camino recorrido, y sirven para darle contexto al presente y sentido al futuro, no para tratar de recuperar un pasado idealizado.

Cuando digo que "la Internet de antes era mejor" no hablo de volver al Internet Explorer 4; me refiero a que sé lo linda que puede ser una comunidad cuando no está montada sobre una plataforma de marketing y psyops armada por un billonario sociópata, y que necesito más de lo primero y menos de lo segundo en mi vida.

En resumen, este tano hace música muy buena que me gusta mucho.


Cómo apuntar un subdominio de No-IP a un túnel de Cloudflare Zero Trust

Publicado el 22 de Marzo de 2026 en  

Mudé este sitio web a mi laboratorio hogareño porque la instancia de GCP tenía poco almacenamiento. Configurar Cloudflare Zero Trust para responder peticiones de un subdominio gratuito No-IP fue toda una aventura: definitivamente no es un caso de uso que hayan contemplado. Esto es un tutorial de cómo se hace.

La idea básica es configurar el subdominio de No-IP como un hostname alternativo de un dominio válido en Cloudflare para que las peticiones puedan encontrar el camino hacia nuestro túnel.

Requisitos

  • Un túnel gratuito Cloudflare Zero Trust apuntando a donde queremos hospedar el sitio No-IP.
  • Una manera de enrutar las peticiones que llegan a nuestro túnel Zero Trust según la solicitud (ej: un proxy Traefik).
  • Un subdominio gratuito de No-IP (ej: [misubdominio].myddns.me, [misubdominio].ddns.net, etc.)
  • Un dominio cualquiera administrado por Cloudflare con el plan gratuito (ej: [midominio].com), al que le vamos a agregar un subdominio para convencer a Cloudflare de aceptar No-IP como válido.
  • Un servidor web accesible desde Internet que pueda servir archivos de texto en una ruta específica para el desafío HTTP ACME. Por ejemplo, si nuestra PC es visible desde Internet, con eso alcanza. Sino podemos usar una VM en algún servidor cloud o algo por el estilo.

Instrucciones

Configuramos nuestros servidores web:

  • Las peticiones a [subdominiocualquiera].[midominio].com tienen que devolver una respuesta HTTP OK cualquiera, aunque sea un simple 200 OK sin cuerpo. Tiene que ser una respuesta válida para que Cloudflare lo acepte.
  • Las peticiones a [misubdominio].myddns.me que lleguen a nuestro túnel Zero Trust tienen que devolver el servicio que queramos publicar. Con Traefik esto es una línea de configuración.

En Cloudflare:

  • Vamos a Zero Trust -> Networks -> Tunnels & mesh -> [nuestro túnel] -> Published application routes, y agregamos [misubdominio].myddns.me. El selector de dominios parece una lista desplegable, pero en realidad es un cuadro de texto editable, así que podemos ingresar un dominio que no esté vinculado a nuestra cuenta.
  • Vamos a Cloudflare -> Domains -> [midominio].com -> SSL/TLS -> Custom Hostnames, y activamos "Cloudflare for SaaS".
  • Configuramos [subdominiocualquiera].[midominio].com como "fallback hostname". Nos aseguramos de que se valide. Por este motivo el subdominio tiene que devolver una respuesta HTTP OK.

En No-IP:

  • Vamos a DNS Records y configuramos temporalmente nuestro subdominio de No-IP con un registro CNAME apuntando a un servidor web cualquiera que controlemos nosotros. Si solamente tenemos una IP pública (ej: de una VM de GCP), podemos usar un servicio gratuito como nip.io que enruta automáticamente las peticiones a la IP especificada como subdominio (ej: 11.22.33.44.nip.io).

En Cloudflare:

  • Vamos a Cloudflare -> Domains -> [midominio].com -> SSL/TLS -> Custom Hostnames y agregamos un nombre de host personalizado [misubdominio].myddns.me a nuestro dominio. Esto requiere responder al desafío ACME. Seleccionamos la validación por HTTP.
  • Validamos el hostname respondiendo el desafío, que es un simple GET HTTP que tiene que devolver una cadena específica que te da Cloudflare. Podemos levantar un servidor web y servir los archivos de texto en la URL /.well-known/acme-challenge/caracteresaleatorios.

En No-IP:

  • Cambiamos el registro CNAME y lo apuntamos a [subdominiocualquiera].[midominio].com.

¡Listorti!

Resultado

Las peticiones a [misubdominio].myddns.me siguen el CNAME hacia [subdominiocualquiera].[midominio].com. Cloudflare reconoce tanto el origen real (No-IP) como el CNAME como rutas válidas (porque el subdominio de No-IP es un custom hostname del otro dominio), así que reenvía la petición a nuestro túnel. Nuestro servidor recibe una solicitud a subdominio.myddns.me y nuestro proxy Traefik o similar se encarga de enrutarla.

¡Bonus! Cloudflare se encarga del TLS. No hace falta Certbot: el servidor hogareño puede responder HTTP. 

Servir múltiples servicios a diferentes subdominios de No-IP

Podemos agregar cualquier cantidad de subdominios de No-IP como custom hostnames de nuestro dominio (hay que responder el desafío ACME por cada uno) y configurarlos con un CNAME apuntando a [subdominiocualquiera].[midominio].com. Nuestro servidor web ve peticiones a los distintos subdominios de No-IP y puede enrutarlos a diferentes servicios según corresponda.


SFTP en Google Compute Engine

Publicado el 25 de Junio de 2025 en  

Este sitio corre en una instancia de Google Compute Engine. Todo este tiempo estuve usando shell scripts para subir y bajar archivos con la herramienta de SCP de Google Tools, pero resulta que hay una forma mucho más simple: el firewall de GCP tiene abierto el puerto 22 por defecto, así que se puede entrar por SFTP con FileZilla usando el archivo de clave privada de SSH. Ahora me siento tonto.


Social Archivist: aceptando links por e-mail

Publicado el 1 de Mayo de 2025 en    

Agregué la función de recibir links por e-mail: el usuario puede escribir a [su id]@socialarchivist.online para guardar contenidos.

Para esto creé un email worker de CloudFlare que captura todos los mails que llegan al dominio. Es básicamente una función lambda con acceso al contenido raw del email entrante. Literalmente 20 líneas de código:

export default {
  async email(message, env, ctx) {
    const webhookUrl = "https://socialarchivist.online/.../";

    try {
      const emailData = {
        from: message.from,
        to: message.to,
        headers: Object.fromEntries(message.headers),
        raw: await new Response(message.raw).text(),
        rawSize: message.rawSize,
      };

      const requestOptions = {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(emailData),
      };

      await fetch(webhookUrl, requestOptions);
    } catch (error) {
      console.error(`Error processing email from ${message.from}: ${error.message}`);
      message.setReject(`Internal worker error processing email: ${error.message}`);
    }
  }
}

La teoría de la fealdad de Brian Eno

Publicado el 24 de Marzo de 2025 en  

Todo lo que parezca extraño, feo, incómodo y desagradable en un nuevo medio de expresión sin duda se convertirá en su identidad. La distorsión del CD, el temblor del video digital, el sonido pobre de 8 bits... Todo esto será celebrado y emulado tan pronto pueda ser evitado. Es el sonido del fracaso: gran parte del arte moderno es el sonido de las cosas saliéndose de control, de un medio llevado hasta sus límites y rompiéndose. La distorsión de la guitarra es el sonido de algo demasiado potente para el medio que debía contenerlo. El cantante de blues con la voz quebrada es el sonido de un grito emocional demasiado poderoso para la garganta que lo libera. La emoción del granulado del film, del blanco y negro lavado, es la emoción de contemplar eventos demasiado trascendentales para el medio asignado para registrarlos.

— Brian Eno, A Year With Swollen Appendices, 1996

Este fragmento de la autobiografía de Eno (productor inglés de música, especialmente electrónica) se viraliza cada pocos meses bajo la consigna de "la teoría de la fealdad".

La idea de belleza en la cultura surge como una celebración de lo que es propicio para la vida humana: los cazadores de las pinturas rupestres, las fértiles curvas de las venus paleolíticas, la robusta simetría de las pirámides. Esa concepción proyecta una enorme sombra sobre el universo, relegando lo asimétrico, lo frágil y lo inútil a la categoría de feo: distante, huérfano, ajeno.

Lo no-nosotros.

Entonces aparece el artista y fija su atención en esa fealdad. No la ignora ni la oculta, sino que la observa con detenimiento, con una mezcla de fascinación y desafío. En lugar de rechazarla, la abraza y la convierte en su materia prima. A partir de ella, crea una obra, que invariablemente genera rechazo y ridículo. Pero en ese acto ocurre un proceso alquímico, ya que incluso a través de la burla, lo que había sido desterrado de nuestra concepción antropocéntrica de la realidad penetra su muralla semántica y pasa formar parte de la cultura.

Pronto el ojo y el oído se acostumbran, y lo que antes nos resultaba ajeno empieza a sentirse familiar. Con el tiempo, cuando el futuro deviene en pasado, lo que fue despreciado pasa a ser objeto de nostalgia. Y finalmente, en un giro irónico, lo que fue feo se vuelve bello.

¿De qué se ríe la cultura hoy?


MilkDrop3: visualizaciones con cualquier reproductor

Publicado el 21 de Marzo de 2025 en  

MilkDrop3 es un programa para Windows que usa el motor de MilkDrop2, el venerable plugin de Winamp, para generar visualizaciones de cualquier cosa que esté sonando en el sistema: YouTube, Spotify o lo que sea. Es compatible con los archivos de visualizaciones de MilkDrop2 y funciona en Windows modernos.


Cloudflare y GoDaddy, un solo corazón

Publicado el 16 de Marzo de 2025 en  

Tengo varios sitios viejos (15+ años) hospedados en GoDaddy usando Cloudflare para habilitar SSL. Hoy estaba haciendo mantenimiento y encontré una catarata de problemas, pero quiero hablar de dos, para cuando Faust del futuro invariablemente se los tope de nuevo.

1) Con HTTPS, veía la página de otra persona

Entro a mi sitio por HTTP: todo perfecto. Entro a mi sitio por HTTPS: veo la web de un actor estadounidense. ¿Finalmente perdí la poca cordura que me quedaba? No: resulta que si un sitio en GoDaddy no tiene configurado un certificado SSL, se vuelve loco y devuelve a Cloudflare otro sitio cualquiera. Por suerte GoDaddy permite crear certificados SSL autofirmados, y eso resuelve el problema.

2) E-mailn't

Mando un mail desde Gmail a uno de mis dominios. No llega. Recibo una autorespuesta:

550 Please turn on SMTP Authentication in your mail client. mail-vk1-f170.google.com [209.85.221.170]:61827 is not permitted to relay through this server without authentication.

Reviso los DNS records: MX, CNAME, SRV. Todo perfecto.

Estuve dos horas googleando para descubrir que a veces AndáPapito decide espontáneamente habilitar el enrutamiento de e-mails para un dominio. ¿Lo hace a propósito? Como sea, cpanel / e-mail routing / direct e-mail, y resuelto. 


CarpiScript: lenguaje de scripting en castellano

Publicado el 16 de Marzo de 2025 en    

CarpiScript es un lenguaje de scripting en castellano desarrollado en TypeScript. Incluye analizador léxico, analizador sintáctico e intérprete. Lo armé como experimento para aprender sobre desarrollo de lenguajes de programación y practicar un poco de TS. El próximo paso lógico sería hacer un compilador.


¡Hola, mundo!

Publicado el 15 de Marzo de 2025 en  
Actualizado el 15 de Marzo de 2026

Esta es mi nueva página web personal. Está desarrollada en FrontPage 2003 (el cenit del diseño web) con PHP (la tecnología backend definitiva) y corre en una instancia e2-micro de Google Compute Engine (cringe). Invitados especiales: Nginx y Certbot. ¡Espero que les guste!

Edit 2026: es exactamente un año después y la instancia e2-micro me quedó corta, así que mudé el sitio a mi laboratorio hogareño detrás de un túnel Cloudflare Zero Trust, y le di una lavada de cara para que se viera peor que antes.


Versión 2025


Versión 2026