> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Almacenamiento propio (S3 autohospedado)

> Alternativas de código abierto a S3/R2 para el flujo BYOS (input_url/output_url), sin depender de un proveedor de nube.

# Almacenamiento propio (S3 autohospedado)

La secuencia BYOS (`url` entrada + `output_url` salida) solo necesita almacenamiento que hable **el protocolo S3** (PUT/GET con URL prefirmadas). No tiene que ser AWS S3 o Cloudflare R2: cualquier servidor compatible con S3 que pueda ejecutar usted mismo funciona igual, con el mismo AWS/S3 SDK que ya conoce para generar URL firmadas.

<Note>
  API de markpdf no sabe ni le importa qué implementación de almacenamiento hay detrás de `url`/`output_url`. Solo necesita un URL firmado para hacer `GET` (entrada) o `PUT` (salida). Todo lo que aparece en esta página sucede en **su** infraestructura, no dentro de API.
</Note>

## Cuando te conviene

* Quiere mantener los documentos de sus usuarios en su propio VPS/centro de datos por motivos de cumplimiento o costos.
* Ya tienes un servidor con mucho disco/red y no quieres pagar la salida de un proveedor de nube.
* Estás en el desarrollo local y no quieres depender de las credenciales de un grupo real.

Si ya usa S3, R2, GCS o Supabase Storage y le funciona bien, no es necesario migrar; esta página es para aquellos que desean evitar esa dependencia.

## Opciones compatibles con S3 que puedes autohospedar

| Proyecto                                                             | Cuando elegirlo                                                                                                                                                    |
| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **[MinIO](https://min.io/)**                                         | La opción más popular. S3 API casi 1:1, binario único, consola web incluida. El mejor punto de partida si nunca ha configurado su propio almacenamiento.           |
| **[Garaje](https://garagehq.deuxfleurs.fr/)**                        | Escrito en Rust, destinado a clústeres pequeños o distribuidos geográficamente con hardware modesto. Buena opción si quieres replicación entre varios VPS baratos. |
| **[AlgasFS](https://github.com/seaweedfs/seaweedfs)**                | Optimizado para muchos archivos pequeños/medianos con una sobrecarga mínima por objeto. Incluye puerta de enlace S3.                                               |
| **[Cef (RGW)](https://docs.ceph.com/en/latest/radosgw/)**            | Para cuando ya tenga, o planee tener, un gran grupo Ceph; RADOS Gateway expone el S3 API encima de él. Más pesado de operar que las opciones anteriores.           |
| **[Servidor de nube Zenko](https://github.com/scality/cloudserver)** | S3 API en Node.js, útil si su pila ya es toda JS y desea un backend de almacenamiento integrable/de prueba con almacenamiento en disco o memoria.                  |

Todos exponen el mismo protocolo, por lo que el código que genera las URL firmadas (`getSignedUrl` / `generate_presigned_url`) es intercambiable entre ellas y S3/R2. Solo cambia `endpoint`, credenciales y `forcePathStyle`/`s3ForcePathStyle`.

## Genere URL firmadas en su propio servidor

Utilice el AWS SDK normal que apunta al `endpoint` de su servidor autohospedado. Funciona igual con MinIO, Garage, SeaweedFS o Ceph RGW.

<CodeGroup>
  ```ts Node.js (aws-sdk v3) theme={null}
  import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";

  const s3 = new S3Client({
    endpoint: "https://storage.your-server.com", // MinIO/Garage/SeaweedFS/Ceph RGW
    region: "us-east-1", // valor arbitrario, la mayoría lo ignora
    forcePathStyle: true, // requerido por MinIO/Garage/SeaweedFS
    credentials: { accessKeyId: "...", secretAccessKey: "..." },
  });

  const inputUrl = await getSignedUrl(
    s3,
    new GetObjectCommand({ Bucket: "documents", Key: "report.pdf" }),
    { expiresIn: 900 },
  );

  const outputUrl = await getSignedUrl(
    s3,
    new PutObjectCommand({ Bucket: "documents", Key: "report.md" }),
    { expiresIn: 900 },
  );
  ```

  ```python Python (boto3) theme={null}
  import boto3

  s3 = boto3.client(
      "s3",
      endpoint_url="https://storage.your-server.com",
      aws_access_key_id="...",
      aws_secret_access_key="...",
  )

  input_url = s3.generate_presigned_url(
      "get_object", Params={"Bucket": "documents", "Key": "report.pdf"}, ExpiresIn=900
  )

  output_url = s3.generate_presigned_url(
      "put_object", Params={"Bucket": "documents", "Key": "report.md"}, ExpiresIn=900
  )
  ```
</CodeGroup>

Con las URL generadas, el flujo es el mismo de siempre:

```json theme={null}
{
  "url": "https://storage.your-server.com/documents/report.pdf?X-Amz-Signature=...",
  "output_url": "https://storage.your-server.com/documents/report.md?X-Amz-Signature=...",
  "output_encoding": "zstd"
}
```

Consulte [`output_url`](/docs/public/es/api/parameters#output_url) y [Compresión de salida](/docs/public/es/concepts/compression#output-compression-output_encoding).

## Notas prácticas

<Tip>
  `forcePathStyle` (o `s3ForcePathStyle` en SDK más antiguos) casi siempre es necesario contra MinIO/Garage/SeaweedFS: estos servidores no admiten el estilo `bucket.endpoint` de forma predeterminada, solo `endpoint/bucket`.
</Tip>

<Warning>
  `https` debe poder acceder a su servidor de almacenamiento desde donde se ejecuta API (no `localhost` o una IP privada, a menos que haya implementado API en su propia red). Si el host no se puede resolver públicamente, API no podrá hacer `GET`/`PUT` contra `url`/`output_url`, y la conversión fallará con `400`/`502`.
</Warning>

* Coloque TLS delante de su servidor de almacenamiento (proxy inverso con Let's Encrypt, por ejemplo): las URL firmadas viajan a través de la red pública.
* Vence las URL firmadas en el tiempo mínimo razonable (minutos, no días); genera uno nuevo para cada conversión.
* Si migra desde S3/R2 a su propio almacenamiento más adelante, solo cambie `endpoint` y las credenciales; el resto del código (URL firma, llamando a API) no cambia.
