Servidor propio

Layers en tu servidor

Una aplicación y unos pocos servicios en un solo docker-compose.yml. También se instala en una red aislada; tus datos nunca salen de tu infraestructura.

La imagen hissih/layers en Docker Hub: más de 20.000 descargas. 90 días de prueba; después se necesita una licencia.

God Mode → Primeros pasos: lista de comprobación del servidor, 4 de 6 hechos: dominios, correo, módulos, IA

Instalar con IA

Instálalo con un asistente de IA

Pega el prompt en ChatGPT, Claude o cualquier otro asistente. Lee la guía completa y te acompaña paso a paso: dominio, correo, llamadas, editores, modelo de IA.

Guía para IA y para personas: layers.md/self-hosted/llms.txt

Prompt
Ayúdame a instalar Layers en mi servidor. Guía: https://layers.md/self-hosted/llms.txt (versión completa: https://layers.md/self-hosted/llms-full.txt). Guíame paso a paso y pregúntame primero por el dominio, el correo y el modelo de IA.

De qué está hecho

Una aplicación y unos pocos servicios a su lado

Todo va en un solo docker-compose.yml. Solo Layers y PostgreSQL son obligatorios; añade el resto según lo necesites.

Obligatorio

Layers

8080

Todo el espacio de trabajo, la API y el panel de administración God Mode: una sola aplicación Java, un solo puerto.

Obligatorio

PostgreSQL

5432

Base de datos. Incluida en compose, o tu propio servidor externo.

Recomendado

Búsqueda: Manticore

9308

Búsqueda en tareas, páginas, chats y archivos. Manticore es un contenedor ligero aparte, ya incluido en compose. ¿Ya tienes un clúster de Elasticsearch? Layers también funciona con él.

Sin un servicio aparte: Lucene integrado, con el índice en el directorio de archivos.

Para llamadas

LiveKit

7880 · 7881 · UDP 50000–50100

Llamadas y compartir pantalla. Para grabar hace falta el servicio LiveKit Egress, que se instala aparte.

Sin él, no hay llamadas; los chats siguen funcionando.

Para el correo

SMTP

587 / 465

Tu servidor de correo: invitaciones, confirmación de registro, restablecimiento de contraseña.

Sin él, no se envían correos.

Para documentos

SumDoc

8090 · /sumdoc

Edita .docx en el navegador.

Sin él, los documentos se abren como una tarjeta de descarga.

Para hojas de cálculo

SumSheet

8092 · /sumsheet

Edita .xlsx y .xlsm en el navegador.

Sin él, las hojas de cálculo se abren como una tarjeta de descarga.

Opcional

Servidor MCP

8091 · /mcp

El asistente y los agentes externos (Claude, Cursor) trabajan con tareas y páginas.

Sin él, el asistente solo responde, sin realizar acciones.

Opcional

LLM

/v1 compatible con OpenAI

Tu modelo: Ollama o cualquier servidor compatible con OpenAI, o un proveedor en la nube con clave.

Sin él, no hay funciones de IA.

Linux x86-64 + Docker

Las imágenes se compilan para linux/amd64. Docker y Docker Compose.

Layers: 2 GB de RAM

Mínimo para la aplicación, más PostgreSQL.

Editores: 2 GB + ~250 MB

por documento abierto. Cada documento se ejecuta en su propio proceso.

Archivos de hasta 900 MB

por defecto. El límite se cambia con una variable.

Instalación

De un servidor vacío al primer inicio de sesión

Trece pasos, cada uno con código listo. Las contraseñas y claves de los ejemplos son marcadores change-me: sustitúyelos.

  1. 1

    Prepara el servidor

    Linux x86-64 con Docker y Docker Compose. Un dominio que apunte al servidor, por ejemplo example.com. Dentro de una red privada puedes usar http://IP:8080 en su lugar.

    Genera tu propia contraseña para cada marcador change-me.

    Terminal
    docker --version
    docker compose version
    mkdir -p /opt/layers && cd /opt/layers
    openssl rand -hex 24
  2. 2

    Crea docker-compose.yml

    Un solo archivo para toda la instalación. Sustituye example.com por tu dominio y cada change-me-… por tu propio valor. Quita los servicios que no necesites (llamadas, editores, MCP) junto con sus variables.

    El volumen de Layers debe ser /root/layers_data: ahí viven los archivos, la configuración de God Mode y los secretos. La contraseña de la base de datos aparece dos veces y debe coincidir; lo mismo con la clave de LiveKit.

    Las imágenes de Layers y de los editores están en Docker Hub: hissih/layers.

    docker-compose.yml
    name: layers
    
    services:
      db:
        image: postgres:16-alpine
        restart: unless-stopped
        environment:
          POSTGRES_DB: layers
          POSTGRES_USER: layers
          POSTGRES_PASSWORD: change-me-db
        volumes:
          - pg_data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U layers -d layers"]
          interval: 5s
          retries: 20
    
      manticore:
        image: manticoresearch/manticore:latest
        restart: unless-stopped
        volumes:
          - manticore_data:/var/lib/manticore
    
      layers:
        image: hissih/layers:latest
        platform: linux/amd64
        restart: unless-stopped
        depends_on:
          db: { condition: service_healthy }
          manticore: { condition: service_started }
        ports:
          - "127.0.0.1:8080:8080"
        volumes:
          - layers_data:/root/layers_data
        environment:
          SPRING_PROFILES_ACTIVE: server
          # Base de datos
          DB_SERVER_URL: jdbc:postgresql://db:5432/layers
          DB_SERVER_USERNAME: layers
          DB_SERVER_PASSWORD: change-me-db
          # Panel de administración /godmode
          ADMIN_USERNAME: admin
          ADMIN_PASSWORD: change-me-admin
          # Dominio
          LAYERS_DOMAIN: example.com
          LAYERS_APP_DOMAIN: example.com
          APP_REDIRECT_URL: https://example.com
          APP_CORS_ALLOWED_ORIGINS: https://example.com
          # Búsqueda
          SEARCH_PROVIDER: manticore
          SEARCH_MANTICORE_ENABLED: "true"
          SEARCH_MANTICORE_URL: http://manticore:9308
          SEARCH_ELASTICSEARCH_ENABLED: "false"
          # Llamadas
          LIVEKIT_ENABLED: "true"
          LIVEKIT_URL: ws://livekit:7880
          LIVEKIT_PUBLIC_URL: wss://example.com/livekit/
          LIVEKIT_API_KEY: change-me-livekit-key
          LIVEKIT_API_SECRET: change-me-livekit-secret-min-32-characters
          # Correo (saliente)
          MAIL_PROVIDER: smtp
          SPRING_MAIL_HOST: smtp.example.com
          SPRING_MAIL_PORT: "587"
          SPRING_MAIL_USERNAME: noreply@example.com
          SPRING_MAIL_PASSWORD: change-me-smtp
          SPRING_MAIL_SMTP_AUTH: "true"
          SPRING_MAIL_SMTP_STARTTLS_ENABLE: "true"
          # Editores SumDoc y SumSheet (direcciones que ve el navegador)
          LAYERS_FASTDOC_URL: https://example.com/sumdoc
          LAYERS_FASTSHEET_URL: https://example.com/sumsheet
          # IA: el modelo se conecta en God Mode → IA
          AI_ENABLED: "true"
          ASSISTANT_MCP_URL: http://mcp:8091/mcp
          AI_USER_MODEL_CHOICE: "true"
    
      livekit:
        image: livekit/livekit-server:latest
        restart: unless-stopped
        environment:
          LIVEKIT_CONFIG: |
            port: 7880
            bind_addresses: [""]
            rtc:
              tcp_port: 7881
              port_range_start: 50000
              port_range_end: 50100
              use_external_ip: true
            keys:
              change-me-livekit-key: change-me-livekit-secret-min-32-characters
            webhook:
              api_key: change-me-livekit-key
              urls:
                - http://layers:8080/v1/livekit/webhook
        ports:
          - "7880:7880"
          - "7881:7881"
          - "7882:7882/udp"
          - "50000-50100:50000-50100/udp"
    
      sumdoc:
        image: hissih/sumdoc-webhost:latest
        platform: linux/amd64
        restart: unless-stopped
        command: ["--base-path", "/sumdoc", "--max-cabins", "12", "--idle-min", "20",
                  "--auth-url", "http://layers:8080/v1/me",
                  "--attach-url", "http://layers:8080/v1/attachments/{id}/content"]
        ports:
          - "127.0.0.1:8090:8090"
        volumes:
          - sumdoc_cabins:/data/cabins
    
      sumsheet:
        image: hissih/sumsheet-webhost:latest
        platform: linux/amd64
        restart: unless-stopped
        command: ["--base-path", "/sumsheet", "--max-cabins", "12", "--idle-min", "20",
                  "--auth-url", "http://layers:8080/v1/me",
                  "--attach-url", "http://layers:8080/v1/attachments/{id}/content"]
        ports:
          - "127.0.0.1:8092:8092"
        volumes:
          - sumsheet_cabins:/data/cabins
    
      mcp:
        image: layers-mcp-server:from-distribution   # Imagen del servidor MCP de la distribución
        restart: unless-stopped
        depends_on: [layers]
        environment:
          SPRING_PROFILES_ACTIVE: server
          SERVER_PORT: "8091"
          LAYERS_BACKEND_BASEURL: http://layers:8080
          LAYERS_BACKEND_FORWARDINCOMINGAUTH: "true"
          LAYERS_BACKEND_REQUIREINCOMINGAUTH: "true"
        ports:
          - "127.0.0.1:8091:8091"
    
    volumes:
      pg_data:
      manticore_data:
      layers_data:
      sumdoc_cabins:
      sumsheet_cabins:
  3. 3

    Elige el motor de búsqueda

    Layers incluye tres motores de búsqueda, que se eligen con SEARCH_PROVIDER (auto, manticore, elasticsearch, lucene).

    Manticore, el recomendado: un contenedor ligero aparte, ya incluido en compose. Elasticsearch, si tu empresa ya tiene un clúster: quita el servicio manticore y configura las variables de la derecha. Lucene, integrado, sin servicio aparte: el índice vive en el directorio de archivos de Layers (el volumen layers_data); adecuado para una instalación pequeña.

    Tras cambiar de motor, reindexa: God Mode → Plataforma → Búsqueda.

    Elasticsearch: en lugar de las variables de Manticore en layers
          SEARCH_PROVIDER: elasticsearch
          SEARCH_ELASTICSEARCH_ENABLED: "true"
          SEARCH_MANTICORE_ENABLED: "false"
          ES_HOST: elasticsearch.example.com
          ES_PORT: "9200"
          ES_PROTOCOL: https
          ES_USERNAME: layers
          ES_PASSWORD: change-me-es
    Lucene: integrado, sin el servicio manticore
          SEARCH_PROVIDER: lucene
          SEARCH_MANTICORE_ENABLED: "false"
          SEARCH_ELASTICSEARCH_ENABLED: "false"
  4. 4

    Arráncalo

    El primer arranque tarda entre 15 y 60 segundos. Cuando el log muestre Started LayersApplication, abre tu dominio y crea la primera cuenta.

    Terminal
    docker compose up -d
    docker compose logs -f layers
    curl -s http://127.0.0.1:8080/health
    # OK
  5. 5

    Activa HTTPS con nginx

    Layers sirve todo desde el puerto 8080: nginx solo termina TLS y hace de proxy para las llamadas, los editores y MCP. Los editores se quedan en el mismo dominio para recibir la cookie de inicio de sesión.

    Sin limit_req ni archivos estáticos en nginx: la protección contra fuerza bruta y la caché vienen integradas.

    Terminal
    sudo apt install nginx certbot python3-certbot-nginx
    sudo certbot certonly --nginx -d example.com
    /etc/nginx/sites-available/layers.conf
    server {
        listen 80;
        server_name example.com;
        location /.well-known/acme-challenge/ { root /var/www/certbot; }
        location / { return 301 https://example.com$request_uri; }
    }
    
    server {
        listen 443 ssl http2;
        server_name example.com;
    
        ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
        client_max_body_size 1000m;
        add_header X-Content-Type-Options nosniff always;
        add_header X-Frame-Options SAMEORIGIN always;
    
        # Layers: web, API, God Mode
        location / {
            proxy_pass http://127.0.0.1:8080;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto https;
            proxy_read_timeout 300;
        }
    
        # LiveKit: señalización (la barra final en proxy_pass importa)
        location /livekit/ {
            proxy_pass http://127.0.0.1:7880/;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 86400s;
        }
        # LiveKit REST: el SDK del servidor llama a /twirp/ en la raíz
        location /twirp/ {
            proxy_pass http://127.0.0.1:7880/twirp/;
            proxy_set_header Host $host;
            proxy_read_timeout 86400s;
        }
    
        # Editores: en el mismo dominio, para que reciban la cookie de inicio de sesión de Layers
        location /sumdoc {
            proxy_pass http://127.0.0.1:8090;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 3600s;
        }
        location /sumsheet {
            proxy_pass http://127.0.0.1:8092;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 3600s;
        }
    
        # MCP para agentes externos (opcional)
        location /mcp {
            proxy_pass http://127.0.0.1:8091/mcp;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_buffering off;
            proxy_read_timeout 3600s;
        }
    }
    Terminal
    sudo ln -s /etc/nginx/sites-available/layers.conf /etc/nginx/sites-enabled/
    sudo rm -f /etc/nginx/sites-enabled/default
    sudo nginx -t && sudo systemctl reload nginx
  6. 6

    Abre los puertos para las llamadas

    LiveKit transmite audio y video por UDP. En God Mode → Aplicaciones → Integraciones → LiveKit indica wss://example.com/livekit/ (la barra final es obligatoria), prueba la conexión y actívalo.

    Si el log dice could not validate external IP, sustituye use_external_ip: true por node_ip: <IP del servidor>. Usa node_ip también en una red cerrada.

    Terminal
    sudo ufw allow 7881/tcp
    sudo ufw allow 7882/udp
    sudo ufw allow 50000:50100/udp
  7. 7

    Configura el correo

    Los correos de Layers (invitaciones, confirmaciones, restablecimiento de contraseñas) salen por tu servidor SMTP desde compose. La misma configuración está en God Mode → Plataforma → Correo, con correo de prueba y plantillas.

    El módulo Correo es un cliente dentro de Layers: las personas conectan los buzones que ya tienen por IMAP y SMTP. Layers no tiene servidor de correo propio.

    Puerto 465 en lugar de 587
    SPRING_MAIL_PORT: "465"
    SPRING_MAIL_SMTP_STARTTLS_ENABLE: "false"
    SPRING_MAIL_PROPERTIES_MAIL_SMTP_SSL_ENABLE: "true"
  8. 8

    Comprueba los editores SumDoc y SumSheet

    El editor no guarda usuarios ni archivos: pregunta a Layers quién es la persona (/v1/me) y obtiene el archivo por id (/v1/attachments). skewMin: 0 significa que el editor y el motor son de la misma versión.

    Un documento abierto ocupa unos 250 MB de RAM; --max-cabins fija cuántos pueden estar abiertos a la vez.

    Terminal
    curl -s http://127.0.0.1:8090/sumdoc/health     # "status": "ok", "skewMin": 0
    curl -s http://127.0.0.1:8092/sumsheet/health   # "status": "ok", "skewMin": 0
    curl -s https://example.com/v1/capabilities
    # {"fastdoc":{"enabled":true,"url":"https://example.com/sumdoc"},
    #  "fastsheet":{"enabled":true,"url":"https://example.com/sumsheet"}, …}
  9. 9

    Conecta un modelo de IA

    God Mode → IA → Proveedores → Añadir proveedor. Los tipos integrados son OpenAI, OpenRouter y Gonka; Conexión propia acepta cualquier servidor con API compatible con OpenAI, como Ollama, sin clave. «Probar» muestra los modelos del servidor: elige los que quieras y actívalos.

    Nuestra instalación de prueba usa así un Ollama local con gpt-oss:20b. La dirección debe ser accesible desde el contenedor de Layers: un nombre de servicio o una IP, no localhost.

    docker-compose.yml: añadir en services
      ollama:
        image: ollama/ollama:latest
        restart: unless-stopped
        volumes:
          - ollama_data:/root/.ollama
    # y en volumes:
    #   ollama_data:
    Terminal
    docker compose up -d ollama
    docker compose exec ollama ollama pull gpt-oss:20b
    # dirección para «Conexión propia»: http://ollama:11434/v1
    # Servidor compatible con OpenAI en otra máquina: http://<host>:8000/v1
  10. 10

    Conecta agentes por MCP

    El servidor MCP ya está en compose: a través de él, el asistente de Layers realiza acciones (crea tareas, escribe páginas). Los agentes externos (Claude, Cursor y otros) se conectan a https://example.com/mcp con un token personal: en Layers → Configuración → Tokens de acceso, de solo lectura o de lectura y escritura.

    Terminal
    curl -s http://127.0.0.1:8091/actuator/health
    # {"status":"UP", …}
  11. 11

    Inicia sesión en God Mode

    https://example.com/godmode/login, usuario admin y tu ADMIN_PASSWORD. «Primeros pasos» es la lista de comprobación del servidor: dominios, correo, inicio de sesión, módulos, marca. Cada punto se marca como hecho según el estado real del servidor.

    God Mode → Getting started: setup steps — domains, email, sign-in, modules, branding, AI
  12. 12

    Introduce la clave de licencia

    El periodo de prueba es de 90 días; después se necesita una licencia. La clave es una sola línea: God Mode → Sistema → Licencia → «Aplicar clave». Se verifica en el propio servidor.

    Terminal
    curl -s https://example.com/layers/license/status
    # {"mode":"MISSING", …, "node":{"regime":"evaluation","evaluationDaysLeft":89, …}}
    # con una clave: "regime":"full"
  13. 13

    Actualiza y haz copias de seguridad

    Actualizar significa una imagen nueva; la base de datos, los archivos y la configuración se quedan en los volúmenes. Fija una etiqueta en lugar de latest para que las actualizaciones sean predecibles: la etiqueta en uso se muestra en God Mode → Operación → Estado del sistema.

    El panel no tiene copia de seguridad integrada de la base de datos: copia la base de datos con pg_dump, y los archivos y la configuración como un archivo comprimido del volumen layers_data.

    Actualizar
    cd /opt/layers
    docker compose pull
    docker compose up -d
    Copia de seguridad
    cd /opt/layers
    docker compose exec -T db sh -c 'pg_dump -U $POSTGRES_USER $POSTGRES_DB' | gzip > layers-db-$(date +%F).sql.gz
    docker run --rm -v layers_layers_data:/data -v "$PWD":/backup alpine \
      tar czf /backup/layers-data-$(date +%F).tar.gz -C /data .

Red aislada

Instalación en red aislada: imágenes como archivo comprimido

Descarga las imágenes en una máquina con acceso a internet y llévalas como un solo archivo. La licencia se verifica en el propio servidor; el catálogo de módulos se conecta al registro y, dentro de una red cerrada, la IA solo funciona con un modelo local.

Terminal
docker pull hissih/layers:latest
docker save hissih/layers:latest postgres:16-alpine manticoresearch/manticore:latest \
  livekit/livekit-server:latest hissih/sumdoc-webhost:latest hissih/sumsheet-webhost:latest \
  | gzip > layers-images.tar.gz
# en el servidor dentro de la red cerrada
gunzip -c layers-images.tar.gz | docker load
docker compose up -d

Probado en nuestra instalación

Cómo lo tenemos montado

Cada captura y cada video de este sitio salen de una instalación de prueba local con exactamente estos servicios. Los puertos se desplazan a 18xxx para evitar conflictos; Mailpit y GreenMail están solo para probar el correo.

ServicioImagenPuerto en nuestra instalaciónEn tu servidor
Layershissih/layers 2.1.184 (el mismo jar)180808080 detrás de nginx
PostgreSQLpostgres:16-alpine18432db:5432, interno
Manticoremanticoresearch/manticore18308manticore:9308, interno
LiveKitlivekit/livekit-server18880 · 18881 · UDP 18900–189507880 · 7881 · UDP 50000–50100
Mailpit (trampa de correo)axllent/mailpitSMTP 18125tu servidor SMTP
GreenMail (buzones de prueba)greenmail/standalone:2.1.3IMAP 18143 · SMTP 18325los buzones que las personas ya tienen
SumDocsumdoc-webhost18290 · /sumdoc8090 · /sumdoc
SumSheetsumsheet-webhost18292 · /sumsheet8092 · /sumsheet
Servidor MCPde la distribución18091 · /mcp8091 · /mcp
LLMOllama, gpt-oss:20b11434 · /v1tu modelo o proveedor

Comprobado el 2 de octubre de 2026: /health → OK, licencia: prueba (quedan 89 días), editores: status ok, MCP: UP.

Por qué así

Control sin concesiones

Red cerrada

Se instala en una red aislada: las imágenes y las actualizaciones llegan como un archivo comprimido.

Licencia en el servidor

La clave se verifica en el propio servidor.

Sin bloqueos por número de personas

El servidor nunca bloquea a las personas que superan el tamaño de la licencia. Almacenamiento: todo lo que quepa en tu hardware; las cuotas por espacio las fijas tú.

Tu marca

Nombre, logotipo, color, idioma predeterminado y tus propios documentos legales.

Tu IA

OpenAI, OpenRouter, cualquier servidor compatible con OpenAI o modelos locales.

Editores al lado

SumDoc y SumSheet funcionan junto a Layers, también en la red aislada.

Licencia

Precio bajo consulta

Prueba

90 días

90 días a velocidad máxima. Sin clave, el servidor sigue funcionando: las personas y los datos no se limitan, solo la transcripción de reuniones va con retraso.

Licencia

Bajo consulta

Hablaremos del tamaño de tu equipo, el soporte y las condiciones de la licencia.

Hablar del servidor propio

El servidor nunca bloquea a las personas que superan la licencia. La instalación, la implantación, las actualizaciones, la personalización y el soporte van aparte, según lo acordado y de pago.

Preguntas frecuentes

¿Necesito internet?

No: ni para instalar desde un archivo, ni para funcionar, ni para verificar la licencia. La IA en una red cerrada funciona con un modelo local.

¿Cuánto hardware hace falta?

Layers necesita al menos 2 GB de RAM más PostgreSQL. Los editores necesitan 2 GB más unos 250 MB por documento abierto. Las imágenes son linux/amd64.

¿Cómo actualizo?

docker compose pull y docker compose up -d. Los datos y la configuración se quedan en los volúmenes.

¿Cómo hago copias de seguridad?

La base de datos con pg_dump; los archivos y la configuración, como un archivo comprimido del volumen layers_data. El panel no tiene copia de seguridad integrada de la base de datos.

La llamada se conecta, pero no hay audio ni video

El puerto UDP 50000–50100 o 7882 está cerrado en el firewall, o LiveKit no encuentra su IP externa: configura node_ip.

¿Qué pasa tras 90 días sin clave?

El servidor sigue funcionando: el inicio de sesión, las personas y los datos no se limitan, no se cierra nada. Solo se ralentiza la transcripción de reuniones: se ejecuta con prioridad baja y unos segundos de retraso. Con una clave, todo vuelve a la velocidad máxima.

¿Se puede iniciar sesión con el directorio corporativo?

Todavía no: SAML, LDAP y SCIM no están soportados. Está disponible el inicio de sesión con contraseña, Google, Yandex, VK y Gosuslugi.

Reúne el trabajo de tu equipo en Layers

Empieza gratis en la nube, hasta 20 personas y sin límite de tiempo, o instala Layers en tu propio servidor.