{"id":21909919,"url":"https://github.com/robinst/pict-rs","last_synced_at":"2026-05-06T22:31:54.996Z","repository":{"id":264935279,"uuid":"820461648","full_name":"robinst/pict-rs","owner":"robinst","description":"Fork of https://git.asonix.dog/asonix/pict-rs","archived":false,"fork":false,"pushed_at":"2024-06-26T14:10:10.000Z","size":3661,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-15T21:43:15.721Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robinst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-26T14:09:10.000Z","updated_at":"2024-06-26T14:11:50.000Z","dependencies_parsed_at":"2024-11-29T01:20:13.566Z","dependency_job_id":null,"html_url":"https://github.com/robinst/pict-rs","commit_stats":null,"previous_names":["robinst/pict-rs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinst%2Fpict-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinst%2Fpict-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinst%2Fpict-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robinst%2Fpict-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robinst","download_url":"https://codeload.github.com/robinst/pict-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244924801,"owners_count":20532878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-28T17:26:52.255Z","updated_at":"2026-05-06T22:31:54.941Z","avatar_url":"https://github.com/robinst.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pict-rs\n_a simple image hosting service_\n\n## Navigation\n1. [Links](#links)\n2. [Usage](#usage)\n    1. [Running](#running)\n        1. [Commandline](#commandline)\n        2. [Docker](#docker)\n        3. [Bare Metal](#bare-metal)\n            1. [Distro Package](#distro-package)\n            2. [Binary Download](#binary-download)\n            3. [Compile from Source](#compile-from-source)\n            4. [Nix](#nix)\n    2. [Api](#api)\n3. [Administration](#administration)\n    1. [Backups](#backups)\n    2. [0.4 to 0.5 Migration Guide](#0-4-to-0-5-migration-guide)\n        1. [Overview](#overview)\n        2. [Upgrade Configuration](#upgrade-configuration)\n        3. [Configuration Updates](#configuration-updates)\n            1. [Image Changes](#image-changes)\n            2. [Animation Changes](#animation-changes)\n            3. [Video Changes](#video-changes)\n        4. [Upgrading Directly to Postgres](#upgrading-directly-to-postgres)\n    3. [Filesystem to Object Storage Migration](#filesystem-to-object-storage-migration)\n        1. [Troubleshooting](#migration-troubleshooting)\n    4. [Sled to Postgres Migration](#sled-to-postgres-migration)\n4. [Development](#development)\n    1. [Nix Development](#nix-development)\n        1. [With direnv and nix-direnv](#with-direnv-and-nix-direnv)\n        2. [With just Nix](#with-just-nix)\n    2. [Docker Development](#docker-development)\n        1. [With Arch](#with-arch)\n        2. [With Alpine](#with-alpine)\n5. [Contributing](#contributing)\n6. [FAQ](#faq)\n    1. [Is pict-rs stateless?](#question-is-pict-rs-stateless)\n    2. [Can I use a different database?](#question-can-i-use-a-different-database-with-pict-rs)\n    3. [How can I submit changes?](#question-how-can-i-submit-changes)\n    4. [I want to configure with $format](#question-i-want-to-configure-it-with-yaml-instead-of-toml)\n    5. [How do I donate?](#question-how-do-i-donate-to-pict-rs)\n7. [Common Problems](#common-problems)\n8. [License](#license)\n\n## Links\n- Find the code on [gitea](https://git.asonix.dog/asonix/pict-rs)\n- Join the discussion on [matrix](https://matrix.to/#/#pictrs:matrix.asonix.dog?via=matrix.asonix.dog)\n- Hit me up on [mastodon](https://masto.asonix.dog/@asonix)\n\n## Usage\n### Running\n#### Commandline\n```\n$ pict-rs -h\nA simple image hosting service\n\nUsage: pict-rs [OPTIONS] \u003cCOMMAND\u003e\n\nCommands:\n  run            Runs the pict-rs web server\n  migrate-store  Migrates from one provided media store to another\n  migrate-repo   Migrates from one provided repo to another\n  help           Print this message or the help of the given subcommand(s)\n\nOptions:\n  -c, --config-file \u003cCONFIG_FILE\u003e\n          Path to the pict-rs configuration file\n      --old-repo-path \u003cOLD_REPO_PATH\u003e\n          Path to the old pict-rs sled database\n      --old-repo-cache-capacity \u003cOLD_REPO_CACHE_CAPACITY\u003e\n          The cache capacity, in bytes, allowed to sled for in-memory operations\n      --log-format \u003cLOG_FORMAT\u003e\n          Format of logs printed to stdout [possible values: compact, json, normal, pretty]\n      --log-targets \u003cLOG_TARGETS\u003e\n          Log levels to print to stdout, respects RUST_LOG formatting\n      --log-spans\n          Whether to log openning and closing of tracing spans to stdout\n      --console-address \u003cCONSOLE_ADDRESS\u003e\n          Address and port to expose tokio-console metrics\n      --console-buffer-capacity \u003cCONSOLE_BUFFER_CAPACITY\u003e\n          Capacity of the console-subscriber Event Buffer\n      --opentelemetry-url \u003cOPENTELEMETRY_URL\u003e\n          URL to send OpenTelemetry metrics\n      --opentelemetry-service-name \u003cOPENTELEMETRY_SERVICE_NAME\u003e\n          Service Name to use for OpenTelemetry\n      --opentelemetry-targets \u003cOPENTELEMETRY_TARGETS\u003e\n          Log levels to use for OpenTelemetry, respects RUST_LOG formatting\n      --save-to \u003cSAVE_TO\u003e\n          File to save the current configuration for reproducible runs\n  -h, --help\n          Print help\n  -V, --version\n          Print version\n```\n\nTry running `help` commands for more runtime configuration options\n```bash\n$ pict-rs run -h\n$ pict-rs run filesystem -h\n$ pict-rs run object-storage -h\n$ pict-rs run filesystem sled -h\n$ pict-rs run filesystem postgres -h\n$ pict-rs run object-storage sled -h\n$ pict-rs run object-storage postgres -h\n```\n\nSee [`pict-rs.toml`](./pict-rs.toml) for more\nconfiguration\n\n##### Example:\nRun with the default configuration\n```bash\n$ ./pict-rs run\n```\nRunning on all interfaces, port 8080, storing data in /opt/data\n```bash\n$ ./pict-rs \\\n    run -a 0.0.0.0:8080 \\\n    filesystem -p /opt/data/files \\\n    sled -p /opt/data/sled-repo\n```\nRunning locally, port 9000, storing data in data/, and converting all uploads to PNG\n```bash\n$ ./pict-rs \\\n    run \\\n        -a 127.0.0.1:9000 \\\n        --media-format png \\\n    filesystem -p data/files \\\n    sled -p data/sled-repo\n```\nRunning locally, port 8080, storing data in data/, and only allowing the `thumbnail` and `identity` filters\n```bash\n$ ./pict-rs \\\n    run \\\n        -a 127.0.0.1:8080 \\\n        --media-filters thumbnail \\\n        --media-filters identity \\\n    filesystem -p data/files \\\n    sled -p data/sled-repo\n```\nRunning from a configuration file\n```bash\n$ ./pict-rs -c ./pict-rs.toml run\n```\nMigrating to object storage from filesystem storage. For more detailed info, see\n[Filesystem to Object Storage Migration](#filesystem-to-object-storage-migration)\n```bash\n$ ./pict-rs \\\n    migrate-store \\\n    filesystem -p data/files \\\n    object-storage \\\n        -a ACCESS_KEY \\\n        -b BUCKET_NAME \\\n        -r REGION \\\n        -s SECRET_KEY\n```\nDumping configuration overrides to a toml file\n```bash\n$ ./pict-rs --save-to pict-rs.toml \\\n    run \\\n    object-storage \\\n        -a ACCESS_KEY \\\n        -b pict-rs \\\n        -r us-east-1 \\\n        -s SECRET_KEY \\\n    sled -p data/sled-repo\n```\n\n#### Docker\nRun the following commands:\n```bash\n# Create a folder for the files (anywhere works)\n$ mkdir ./pict-rs\n$ cd ./pict-rs\n$ mkdir -p volumes/pictrs\n$ sudo chown -R 991:991 volumes/pictrs\n$ wget https://git.asonix.dog/asonix/pict-rs/raw/branch/main/docker/prod/docker-compose.yml\n$ sudo docker-compose up -d\n```\n###### Note\n- pict-rs makes use of the system's temporary folder. This is generally `/tmp` on linux\n\n#### Bare Metal\nThere are a few options for acquiring pict-rs to run outside of docker.\n1. Packaged via your distro of choice\n2. Binary download from [the releases page](https://git.asonix.dog/asonix/pict-rs/tags)\n3. Compiled from source\n\nIf running outside of docker, the recommended configuration method is via the\n[`pict-rs.toml`](./pict-rs.toml) file. When running pict-rs, the file can be passed to the binary as\na commandline argument.\n```bash\n$ pict-rs -c /path/to/pict-rs.toml run\n```\n\n##### Distro Package\nIf getting pict-rs from your distro, please make sure it's a recent version (meaning 0.3.x stable,\nor 0.4.x stable). If it is older, consider using an alternative option for installing pict-rs. I am\ncurrently aware of pict-rs packaged in [the AUR](https://aur.archlinux.org/packages/pict-rs) and\n[nixpkgs](https://search.nixos.org/packages?channel=23.05\u0026from=0\u0026size=50\u0026sort=relevance\u0026type=packages\u0026query=pict-rs),\nbut there may be other distros that package it as well.\n\n##### Binary Download\npict-rs provides precompiled binaries that should work on any linux system for x86_64, aarch64, and\narmv7h on [the releases page](https://git.asonix.dog/asonix/pict-rs/tags). If downloading a binary,\nmake sure that you have the following dependencies installed:\n- `imagemagick` 7\n- `ffmpeg` 5 or 6\n- `exiftool` 12 (sometimes called `perl-image-exiftool`)\n\nThese binaries are called by pict-rs to process uploaded media, so they must be in the `$PATH`\navailable to pict-rs.\n\nA notable issue here is imagemagick 7, which is not packaged in Debian Sid and therefore unavailable\nin any version of Debian or Ubuntu. If you are running an ubuntu or debian system, consider using\nthe [Nix](#nix) installation and run method.\n\nMore information is available in the [Ubuntu and Debian docs](./docs/ubuntu-and-debian.md)\n\n##### Compile from Source\npict-rs can be compiled from source using a recent version of the rust compiler. I do development\nand produce releases on 1.75\n\nLike the Binary Download option, `imagemagick`, `ffmpeg`, and `exiftool` must be installed for\npict-rs to run properly.\n\n##### Nix\npict-rs comes with an associated nix flake. This is useful for the development environment, but can\nalso be used to run a \"production\" version of pict-rs with all the neccessary dependencies already\nprovided.\n\nThe Nix package manager can be installed with [these instructions](https://nixos.org/download.html).\nAfter installation, two experimental features must be enabled: `flake` and `nix-command`. These need\nto be added in `/etc/nix/nix.conf`:\n```\nexperimental-features = nix-command flakes\n```\n\nAfter enabling flakes, you can run `nix build` from the pict-rs source directory. This will produce\na nix package containing pict-rs and its dependencies. It will also create a `result` symlink in the\npict-rs directory that links to the newly built package. The contents of `result` should be a single\nfolder `bin` with a single file `pict-rs` inside. This file is a shell script that invokes the\n`pict-rs` binary with the required `$PATH` to find imagemagick 7, ffmpeg 6, and exiftool. You can\ntreat this shell script as if it were the true pict-rs binary, passing it the same arguments you\nwould pict-rs.\n\nExample:\n```\n./result/bin/pict-rs -c dev.toml run\n```\n\n\n### API\npict-rs offers the following endpoints:\n- `POST /image?{args}` for uploading an image. Uploaded content must be valid multipart/form-data with an\n    image array located within the `images[]` key\n\n    The {args} query serves multiple purpose for image uploads. The first is to provide\n    request-level validations for the uploaded media. Available keys are as follows:\n    - max_width: maximum width, in pixels, allowed for the uploaded media\n    - max_height: maximum height, in pixels, allowed for the uploaded media\n    - max_area: maximum area, in pixels, allowed for the uploaded media\n    - max_frame_count: maximum number of frames permitted for animations and videos\n    - max_file_size: maximum size, in megabytes, allowed\n    - allow_image: whether to permit still images in the upload\n    - allow_animation: whether to permit animations in the upload\n    - allow_video: whether to permit video in the upload\n\n    These validations apply in addition to the validations specified in the pict-rs configuration,\n    so uploaded media will be rejected if any of the validations fail.\n\n    The second purpose for the {args} query is to provide preprocess steps for the uploaded image.\n    The format is the same as in the process.{ext} endpoint. The images uploaded with these steps\n    provided will be processed before saving.\n\n    This endpoint returns the following JSON structure on success with a 201 Created status\n    ```json\n    {\n        \"files\": [\n            {\n                \"delete_token\": \"JFvFhqJA98\",\n                \"file\": \"lkWZDRvugm.jpg\",\n                \"details\": {\n                    \"width\": 800,\n                    \"height\": 800,\n                    \"content_type\": \"image/jpeg\",\n                    \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n                }\n            },\n            {\n                \"delete_token\": \"kAYy9nk2WK\",\n                \"file\": \"8qFS0QooAn.jpg\",\n                \"details\": {\n                    \"width\": 400,\n                    \"height\": 400,\n                    \"content_type\": \"image/jpeg\",\n                    \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n                }\n            },\n            {\n                \"delete_token\": \"OxRpM3sf0Y\",\n                \"file\": \"1hJaYfGE01.jpg\",\n                \"details\": {\n                    \"width\": 400,\n                    \"height\": 400,\n                    \"content_type\": \"image/jpeg\",\n                    \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n                }\n            }\n        ],\n        \"msg\": \"ok\"\n    }\n    ```\n- `POST /image/backgrounded?{args}` Upload an image, like the `/image` endpoint, but don't wait to validate and process it.\n    The {args} query is the same format is the inline image upload endpoint.\n\n    This endpoint returns the following JSON structure on success with a 202 Accepted status\n    ```json\n    {\n        \"uploads\": [\n            {\n                \"upload_id\": \"c61422e1-9294-4f1f-977f-c696b7939467\",\n            },\n            {\n                \"upload_id\": \"62cc707f-725c-44b6-908f-2bd8946c3c29\"\n            }\n        ],\n        \"msg\": \"ok\"\n    }\n    ```\n- `GET /image/download?url={url}\u0026backgrounded=(true|false)` Download an image\n    from a remote server, returning the same JSON payload as the `POST /image` endpoint by default.\n\n    if `backgrounded` is set to `true`, then the ingest processing will be queued for later and the\n    response json will be the same as the `POST /image/backgrounded` endpoint.\n- `GET /image/backgrounded/claim?upload_id={uuid}` Wait for a backgrounded upload to complete, claiming it's result\n    Possible results:\n    - 200 Ok (validation and ingest complete):\n        ```json\n        {\n            \"files\": [\n                {\n                    \"delete_token\": \"OxRpM3sf0Y\",\n                    \"file\": \"1hJaYfGE01.jpg\",\n                    \"details\": {\n                        \"width\": 400,\n                        \"height\": 400,\n                        \"content_type\": \"image/jpeg\",\n                        \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n                    }\n                }\n            ],\n            \"msg\": \"ok\"\n        }\n        ```\n    - 422 Unprocessable Entity (validation or otherwise failure):\n        ```json\n        {\n            \"msg\": \"Error message about what went wrong with upload\"\n        }\n        ```\n    - 204 No Content (Upload validation and ingest is not complete, and waiting timed out)\n        In this case, trying again is fine\n- `GET /image/original/{alias}` Get a full-resolution image. `alias` here is the `file` key from the\n    `/image` endpoint's JSON\n- `GET /image/original?alias={alias}` Get a full-resolution image. `alias` here is the `file` key from\n    the `/image` endpoint's JSON\n    Available source arguments are\n    - `?alias={alias}` Serve an original file by its alias\n    - `?proxy={url}` This `proxy` field can be used to proxy external URLs through the original\n        endpoint. These proxied images are removed from pict-rs some time after their last access.\n        This time is configurable with `PICTRS__MEDIA__RETENTION__PROXY`. See\n        (./pict-rs.toml)[./pict-rs.toml] for more information.\n- `HEAD /image/original/{alias}` Returns just the headers from the analogous `GET` request.\n- `HEAD /image/original?alias={alias}` Returns just the headers from the analogous `GET` request.\n    Available source arguments are\n    - `?alias={alias}` Serve an original file by its alias\n    - `?proxy={url}` This `proxy` field can be used to proxy external URLs through the original\n        endpoint. These proxied images are removed from pict-rs some time after their last access.\n        This time is configurable with `PICTRS__MEDIA__RETENTION__PROXY`. See\n        (./pict-rs.toml)[./pict-rs.toml] for more information.\n- `GET /image/details/original/{alias}` for getting the details of a full-resolution image.\n    The returned JSON is structured like so:\n    ```json\n    {\n        \"width\": 800,\n        \"height\": 537,\n        \"content_type\": \"image/webp\",\n        \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n    }\n    ```\n- `GET /image/details/original?alias={alias}` Same as the above endpoint but with a query instead of\n    a path\n\n    Available source arguments are\n    - `?alias={alias}` Serve an original file by its alias\n    - `?proxy={url}` This `proxy` field can be used to get details about proxied images in pict-rs.\n        These proxied images are removed from pict-rs some time after their last access. This time\n        is configurable with `PICTRS__MEDIA__RETENTION__PROXY`. See (./pict-rs.toml)[./pict-rs.toml]\n        for more information.\n- `GET /image/blurhash?alias={alias}` Create and store a blurhash for the provided alias\n\n    Available source arguments are\n    - `?alias={alias}` Serve a blurhash for an image identified by the provided alias\n    - `?proxy={url}` Serve a blurhash for the media hosted at `url`\n        This will download and store the original version of the specified media, as well as its\n        blurhash. Retention for proxied media is configurable with `PICTRS__MEDIA__RETENTION__PROXY`.\n        See (./pict-rs.toml)[./pict-rs.toml] for more information.\n\n    The returned JSON is structured like so:\n    ```json\n    {\n        \"msg\": \"ok\",\n        \"blurhash\": \"LGF5]+Yk^6#M@-5c,1J5@[or[Q6.\"\n    }\n    ```\n- `GET /image/process.{ext}?src={alias}\u0026...` Get a file with transformations applied.\n    Available source arguments are\n    - `?src={alias}` This behavior is the same as in previous releases\n    - `?alias={alias}` This `alias` field is the same as the `src` field. Renamed for better\n        consistency\n    - `?proxy={url}` This `proxy` field can be used to proxy external URLs through the process\n        endpoint. These proxied images are removed from pict-rs some time after their last access.\n        This time is configurable with `PICTRS__MEDIA__RETENTION__PROXY`. See\n        (./pict-rs.toml)[./pict-rs.toml] for more information.\n    \n    Existing transformations include\n    - `identity=true`: apply no changes\n    - `blur={float}`: apply a gaussian blur to the file\n    - `thumbnail={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}`\n        square using raw pixel sampling\n    - `resize={int}`: produce a thumbnail of the image fitting inside an `{int}` by `{int}` square\n        using a Lanczos2 filter. This is slower than sampling but looks a bit better in some cases\n    - `resize={filter}.(a){int}`: produce a thumbnail of the image fitting inside an `{int}` by\n        `{int}` square, or when `(a)` is present, produce a thumbnail whose area is smaller than\n        `{int}`. `{filter}` is optional, and indicates what filter to use when resizing the image.\n        Available filters are `Lanczos`, `Lanczos2`, `LanczosSharp`, `Lanczos2Sharp`, `Mitchell`,\n        and `RobidouxSharp`.\n\n        Examples:\n        - `resize=300`: Produce an image fitting inside a 300x300 px square\n        - `reizie=.a10000`: Produce an image whose area is at most 10000 px\n        - `resize=Mitchell.200`: Produce an image fitting inside a 200x200 px square using the\n            Mitchell filter\n        - `resize=RobidouxSharp.a40000`: Produce an image whose area is at most 40000 px using the\n            RobidouxSharp filter\n    - `crop={int-w}x{int-h}`: produce a cropped version of the image with an `{int-w}` by `{int-h}`\n        aspect ratio. The resulting crop will be centered on the image. Either the width or height\n        of the image will remain full-size, depending on the image's aspect ratio and the requested\n        aspect ratio. For example, a 1600x900 image cropped with a 1x1 aspect ratio will become 900x900. A\n        1600x1100 image cropped with a 16x9 aspect ratio will become 1600x900.\n\n    Supported `ext` file extensions include `apng`, `avif`, `gif`, `jpg`, `jxl`, `png`, and `webp`.\n    Note that while `avif` and `webp` will work for both animated \u0026 non-animated images, some\n    formats like `apng` and `gif` are only used to serve animations while others like `jpg`, `jxl`\n    and `png` are only used to serve sill images.\n\n    An example of usage could be\n    ```\n    GET /image/process.jpg?src=asdf.png\u0026thumbnail=256\u0026blur=3.0\n    ```\n    which would create a 256x256px JPEG thumbnail and blur it\n- `HEAD /image/process.{ext}?src={alias}` Returns just the headers from the analogous `GET` request.\n    Returns 404 if the processed image has not been generated yet.\n\n    Available source arguments are\n    - `?src={alias}` This behavior is the same as in previous releases\n    - `?alias={alias}` This `alias` field is the same as the `src` field. Renamed for better\n        consistency\n    - `?proxy={url}` This `proxy` field can be used to get headers for proxied images.\n- `GET /image/process_backgrounded.{ext}?src={alias}\u0026...` queue transformations to be applied to a\n    given file. This accepts the same arguments as the `process.{ext}` endpoint, but does not wait\n    for the processing to complete.\n\n    Available source arguments are\n    - `?src={alias}` This behavior is the same as in previous releases\n    - `?alias={alias}` This `alias` field is the same as the `src` field. Renamed for better\n        consistency\n- `GET /image/details/process.{ext}?src={alias}\u0026...` for getting the details of a processed image.\n    The returned JSON is the same format as listed for the full-resolution details endpoint.\n\n    Available source arguments are\n    - `?src={alias}` This behavior is the same as in previous releases\n    - `?alias={alias}` This `alias` field is the same as the `src` field. Renamed for better\n        consistency\n    - `?proxy={url}` This `proxy` field can be used to get details about proxied images.\n- `GET /image/details/process.{ext}?alias={alias}` Same as the above endpoint but with a query\n    instead of a path\n\n    Available source arguments are\n    - `?alias={alias}` Serve a processed file by its alias\n    - `?proxy={url}` This `proxy` field can be used to get details about proxied images in pict-rs.\n        These proxied images are removed from pict-rs some time after their last access. This time\n        is configurable with `PICTRS__MEDIA__RETENTION__PROXY`. See [./pict-rs.toml](./pict-rs.toml)\n        for more information.\n- `DELETE /image/delete/{delete_token}/{alias}` or `GET /image/delete/{delete_token}/{alias}` to\n    delete a file, where `delete_token` and `alias` are from the `/image` endpoint's JSON\n- `GET /healthz` Check the health of the pict-rs server. This will check that the `sled` embedded\n    database is functional and that the configured store is accessible\n\n\nThe following endpoints are protected by an API key via the `X-Api-Token` header, and are disabled\nunless the `--api-key` option is passed to the binary or the PICTRS__SERVER__API_KEY environment\nvariable is set.\n\nA secure API key can be generated by any password generator.\n- `POST /internal/import` for uploading an image while preserving the filename as the first alias.\n    The upload format and response format are the same as the `POST /image` endpoint.\n- `POST /internal/delete?alias={alias}` Delete an alias without requiring a delete token.\n    Available source arguments are\n    - `?alias={alias}` Delete a file alias\n    - `?proxy={url}` Delete a proxied file's alias\n\n    This endpoint returns the following JSON\n    ```json\n    {\n        \"msg\": \"ok\",\n    }\n    ```\n- `POST /internal/purge?alias={alias}` Purge a file by it's alias. This removes all aliases and\n    files associated with the query.\n\n    Available source arguments are\n    - `?alias={alias}` Purge a file by it's alias\n    - `?proxy={url}` Purge a proxied file by its URL\n\n    This endpoint returns the following JSON\n    ```json\n    {\n        \"msg\": \"ok\",\n        \"aliases\": [\"asdf.png\"]\n    }\n    ```\n- `GET /internal/aliases?alias={alias}` Get the aliases for a file by its alias\n\n    Available source arguments are\n    - `?alias={alias}` Get all aliases for a file by the provided alias\n    - `?proxy={url}` Get all aliases for a proxied file by its url\n\n    This endpiont returns the same JSON as the purge endpoint\n- `DELETE /internal/variants` Queue a cleanup for generated variants of uploaded images.\n\n    If any of the cleaned variants are fetched again, they will be re-generated.\n- `GET /internal/identifier?alias={alias}` Get the image identifier (file path or object path) for a\n    given alias.\n\n    Available source arguments are\n    - `?alias={alias}` Get the identifier for a file by the provided alias\n    - `?proxy={url}` Get the identifier for a proxied file by its url\n\n    On success, the returned json should look like this:\n    ```json\n    {\n        \"msg\": \"ok\",\n        \"identifier\": \"/path/to/object\"\n    }\n    ```\n- `POST /internal/set_not_found?alias={alias}` Set the 404 image that is served from the original\n    and process endpoints. The image used must already be uploaded and have an alias. The request\n    should look like this:\n    ```json\n    {\n        \"alias\": \"asdf.png\"\n    }\n    ```\n\n    On success, the returned json should look like this:\n    ```json\n    {\n        \"msg\": \"ok\"\n    }\n    ```\n\n    In the event pict-rs can't find the provided alias, it will return a 400 Bad Request with the\n    following json:\n    ```json\n    {\n        \"msg\": \"No hash associated with provided alias\"\n    }\n    ```\n- `POST /internal/export` Export the current sled database to the configured `export_path`. This is\n    useful for taking backups of a running pict-rs server. On success, it will return\n    ```json\n    {\n        \"msg\": \"ok\"\n    }\n    ```\n\n    Restoring from an exported database is as simple as:\n    1. Stopping pict-rs\n    2. Moving your current `sled-repo` directory to a safe location (e.g. `sled-repo.bak`)\n        ```bash\n        $ mv sled-repo sled-repo.bak\n        ```\n    3. Copying an exported database to `sled-repo`\n        ```bash\n        $ cp -r exports/2023-07-08T22:26:21.194126713Z sled-repo\n        ```\n    4. Starting pict-rs\n- `GET /internal/hashes?{query}` Get a page of hashes ordered by newest to oldest based on the\n    provided query. On success, it will return the following json:\n    ```json\n    {\n        \"msg\": \"ok\",\n        \"page\": {\n            \"limit\": 20,\n            \"current\": \"some-long-slug-string\",\n            \"next\": \"some-long-slug-string\",\n            \"prev\": \"some-long-slug-string\",\n            \"hashes\": [{\n                \"hex\": \"some-long-hex-encoded-hash\",\n                \"aliases\": [\n                    \"file-alias.png\",\n                    \"another-alias.png\",\n                ],\n                \"details\": {\n                    \"width\": 1920,\n                    \"height\": 1080,\n                    \"frames\": 30,\n                    \"content_type\": \"video/mp4\",\n                    \"created_at\": \"2022-04-08T18:33:42.957791698Z\"\n                }\n            }]\n        }\n    }\n    ```\n    Note that some fields in this response are optional (including `next`, `prev`, `current`,\n    `details` and `frames`)\n\n    Available query options:\n    - empty: this fetches the first page of the results (e.g. the newest media)\n    - `?slug={slug}` this fetches a specific page of results. the `slug` field comes from the\n        `current`, `next`, or `prev` fields in the page json\n    - `?timestamp={timestamp}` this fetches results older than the specified timestamp for easily\n        searching into the data. the `timestamp` should be formatted according to RFC3339\n    - `?limit={limit}` specifies how many results to return per page\n- `POST /internal/prune_missing?force={force}` Spawn a background task that will check every hash in\n    the database for an associated media file, deleting any record that is missing its media.\n\n    WARNING: This operation is very destructive. Please take backups before invoking it.\n\n    This endpoint can be hit repeatedly to check the progress of the preparations. The returned\n    `progress` value represents how many records have been marked for pruning.\n\n    Optionally, the `force` query parameter can be passed with a value of `true` in order to make\n    pict-rs spawn another task if the current one seems stuck.\n\nAdditionally, all endpoints support setting deadlines, after which the request will cease\nprocessing. To enable deadlines for your requests, you can set the `X-Request-Deadline` header to an\ni128 value representing the number of nanoseconds since the UNIX Epoch. A simple way to calculate\nthis value is to use the `time` crate's `OffsetDateTime::unix_timestamp_nanos` method. For example,\n```rust\n// set deadline of 1ms\nlet deadline = time::OffsetDateTime::now_utc() + time::Duration::new(0, 1_000);\n\nlet request = client\n    .get(\"http://pict-rs:8080/image/details/original/asdfghjkla.png\")\n    .insert_header((\"X-Request-Deadline\", deadline.unix_timestamp_nanos().to_string())))\n    .send()\n    .await;\n```\n\nFinally, there's an optional prometheus scrape endpoint that can be enabled with the\n`PICTRS__METRICS__PROMETHEUS_ADDRESS` configuration. This binds to a separate port from the main\npict-rs application. See [pict-rs.toml](./pict-rs.toml) for more details.\n\n\n## Administration\n### Backups\npict-rs maintains two folders that matter: the `sled-repo` directory, and the `files` directory.\n`sled-repo` is where it keeps metadata about files such as: their location, their aliases, their\nprocessed versions' locations, their dimensions, mime type, etc. `files` is where it puts uploaded\nfiles when storing on the local filesystem.\n\nThe `sled-repo` folder is generally small compared to the `files` folder, and backing it up can be\nas simple as copying the folder somewhere else. I recommend doing so while pict-rs is not running.\n\nIf you can't stop pict-rs, but would like to back up the database, there is an internal endpoint at\n`/internal/export` documented in [Api](#api) that can be used to produce a copy of the current\ndatabase for easy backups.\n\n### 0.4 to 0.5 Migration Guide\n#### Note\nIf you're running an older release of pict-rs, you'll first need to update to 0.4 before continuing\non to 0.5. See the\n[0.3 to 0.4 migration guide](https://git.asonix.dog/asonix/pict-rs/src/branch/v0.4.x#user-content-0-3-to-0-4-migration-guide).\n\n#### Overview\npict-rs will automatically migrate from the 0.4 db format to the 0.5 db format on the first launch\nof 0.5. This process might take a while, especially if you've been running pict-rs since before 0.3.\nThe reason for this is pict-rs now requires original files to have associated details records stored\nin the database, and while generating these records happened by default for 0.3 and 0.4, images\nuploaded before this was standard may not have ever had their details records generated.\n\n#### Upgrade Configuration\nBecause upgrades may take so long, there is a new configuration option introduced to attempt to\nimprove its speed.\n\n```toml\n[upgrade]\nconcurrency = 32\n```\nor\n```\nPICTRS__UPGRADE__CONCURRENCY=32\n```\nor\n```bash\n$ pict-rs run --upgrade-concurrency 32\n```\n\nThis value dictates how many hashes pict-rs will attempt to migrate at the same time. Since this\nvalue will increase the number of concurrent connections to the Repo and the Store, as well as\npotentially increase CPU and memory use, it should be considered carefully before increasing.\n\nFor large-scale deployments, it is likely this value should be bumped to 128, 256, or even 512. The\ndefault value is 32.\n\n#### Configuration Updates\nPreviously, pict-rs only had two categories for files: images and videos. pict-rs 0.5 adds a third\ncategory: animation. With the new explicit support for animated file types, some configuration\noptions have moved.\n\n##### Image Changes\n| Old Environment Variable       | New Environment Variable              |\n| ------------------------------ | ------------------------------------- |\n| `PICTRS__MEDIA__FORMAT`        | `PICTRS__MEDIA__IMAGE__FORMAT`        |\n| `PICTRS__MEDIA__MAX_WIDTH`     | `PICTRS__MEDIA__IMAGE__MAX_WIDTH`     |\n| `PICTRS__MEDIA__MAX_HEIGHT`    | `PICTRS__MEDIA__IMAGE__MAX_HEIGHT`    |\n| `PICTRS__MEDIA__MAX_AREA`      | `PICTRS__MEDIA__IMAGE__MAX_AREA`      |\n|                                | `PICTRS__MEDIA__IMAGE__MAX_FILE_SIZE` |\n\n| Old TOML Value          | New TOML Value                |\n| ----------------------- | ----------------------------- |\n| `[media] format`        | `[media.image] format`        |\n| `[media] max_width`     | `[media.image] max_width`     |\n| `[media] max_height`    | `[media.image] max_height`    |\n| `[media] max_area`      | `[media.image] max_area`      |\n|                         | `[media.image] max_file_size` |\n\n##### Animation Changes\n| Old Environment Variable              | New Environment Variable                    |\n| ------------------------------------- | ------------------------------------------- |\n| `PICTRS__MEDIA__GIF__MAX_WIDTH`       | `PICTRS__MEDIA__ANIMATION__MAX_WIDTH`       |\n| `PICTRS__MEDIA__GIF__MAX_HEIGHT`      | `PICTRS__MEDIA__ANIMATION__MAX_HEIGHT`      |\n| `PICTRS__MEDIA__GIF__MAX_AREA`        | `PICTRS__MEDIA__ANIMATION__MAX_AREA`        |\n| `PICTRS__MEDIA__GIF__MAX_FILE_SIZE`   | `PICTRS__MEDIA__ANIMATION__MAX_FILE_SIZE`   |\n| `PICTRS__MEDIA__GIF__MAX_FRAME_COUNT` | `PICTRS__MEDIA__ANIMATION__MAX_FRAME_COUNT` |\n|                                       | `PICTRS__MEDIA__ANIMATION__FORMAT`          |\n|                                       | `PICTRS__MEDIA__ANIMATION__MAX_FILE_SIZE`   |\n\n| Old TOML Value                | New TOML Value                      |\n| ----------------------------- | ----------------------------------- |\n| `[media.gif] max_width`       | `[media.animation] max_width`       |\n| `[media.gif] max_height`      | `[media.animation] max_height`      |\n| `[media.gif] max_area`        | `[media.animation] max_area`        |\n| `[media.gif] max_file_size`   | `[media.animation] max_file_size`   |\n| `[media.gif] max_frame_count` | `[media.animation] max_frame_count` |\n|                               | `[media.animation] format`          |\n|                               | `[media.animation] max_file_size`   |\n\n##### Video Changes\n| Old Environment Variable             | New Environment Variable                |\n| ------------------------------------ | --------------------------------------- |\n| `PICTRS__MEDIA__ENABLE_SILENT_VIDEO` | `PICTRS__MEDIA__VIDEO__ENABLE`          |\n| `PICTRS__MEDIA__ENABLE_FULL_VIDEO`   | `PICTRS__MEDIA__VIDEO__ALLOW_AUDIO`     |\n| `PICTRS__MEDIA__VIDEO_CODEC`         | `PICTRS__MEDIA__VIDEO__VIDEO_CODEC`     |\n| `PICTRS__MEDIA__AUDIO_CODEC`         | `PICTRS__MEDIA__VIDEO__AUDIO_CODEC`     |\n| `PICTRS__MEDIA__MAX_FRAME_COUNT`     | `PICTRS__MEDIA__VIDEO__MAX_FRAME_COUNT` |\n| `PICTRS__MEDIA__ENABLE_FULL_VIDEO`   | `PICTRS__MEDIA__VIDEO__ALLOW_AUDIO`     |\n|                                      | `PICTRS__MEDIA__VIDEO__MAX_WIDTH`       |\n|                                      | `PICTRS__MEDIA__VIDEO__MAX_HEIGHT`      |\n|                                      | `PICTRS__MEDIA__VIDEO__MAX_AREA`        |\n|                                      | `PICTRS__MEDIA__VIDEO__MAX_FILE_SIZE`   |\n\n| Old TOML Value                | New TOML Value                  |\n| ----------------------------- | ------------------------------- |\n| `[media] enable_silent_video` | `[media.video] enable`          |\n| `[media] enable_full_video`   | `[media.video] allow_audio`     |\n| `[media] video_codec`         | `[media.video] video_codec`     |\n| `[media] audio_codec`         | `[media.video] audio_codec`     |\n| `[media] max_frame_count`     | `[media.video] max_frame_count` |\n| `[media] enable_full_video`   | `[media.video] allow_audio`     |\n|                               | `[media.video] max_width`       |\n|                               | `[media.video] max_height`      |\n|                               | `[media.video] max_area`        |\n|                               | `[media.video] max_file_size`   |\n\nNote that although each media type now includes its own `MAX_FILE_SIZE` configuration, the\n`PICTRS__MEDIA__MAX_FILE_SIZE` value still exists as a global limit for any file type.\n\nIn addition to all the configuration options mentioned above, there are now individual quality\nsettings that can be configured for each image and animation type, as well as for video files.\nPlease see the [pict-rs.toml](./pict-rs.toml) file for more information.\n\n#### Upgrading Directly to Postgres\npict-rs supports migrating directly to the postgres repo during the upgrade. In order to do this,\nthe postgres repo needs to be configured and the `old_repo` needs to be specified. The `old_repo`\nsection just contains the `path` of the `repo` section in your 0.4 config.\n\nExample:\n```toml\n[old_repo]\npath = '/mnt/sled-repo'\n\n[repo]\ntype = 'postgres'\nurl = 'postgres://user:password@host:5432/db'\n```\n\nOr with environment varaibles:\n```\nPICTRS__OLD_REPO__PATH=/mnt/sled-repo\nPICTRS__REPO__TYPE=postgres\nPICTRS__REPO__URL=postgres://user:password@host:5432/db\n```\n\nOnce these variables are set, 0.5 can be started and the migration will automatically occur.\n\n### Filesystem to Object Storage migration\n_Make sure you take a backup of the sled-repo directory before running this command!!! Migrating to\nobject storage updates the database and if you need to revert for any reason, you'll want a backup._\n\nIt is possible to migrate to object storage. This can be useful if\nhosting in a cloud environment, since object storage is generally far cheaper than block storage.\n\nThere's a few required configuration options for object storage. I will try to explain:\n- endpoint: this is the URL at which the object storage is available. Generally this URL will look\n    like `https://\u003cbucket-name\u003e.\u003cregion\u003e.s3.example.com`, but sometimes it could look like\n    `https://\u003cregion\u003e.s3.example.com` or just `https://s3.example.com`\n- bucket-name: this is name of the \"bucket\" in which the objects will reside. A bucket must already\n    exist for the migration to work - pict-rs will not create the bucket on it's own. It is up to\n    you to create a bucket with your storage provider ahead of time.\n- region: this is the \"location\" in which your bucket resides. It may not be meaningful depending on\n    your cloud provider, but it is always required.\n- access-key: this is a secret your cloud provider will give to you in order to access the bucket\n- secret-key: this is a second secret your cloud provider will give to you in order to access the\n    bucket\n\nAdditionally, there's a commandline argument that can be set to change the default level of\nconcurrency for the migration. pict-rs will attempt to migrate 32 hashes at a time, but for large\ndeployments, it may be worth trying to increase this value. Setting it to 128, 256, or even 512\ncould be useful. Note that the bigger this value, the more concurrent connections to the object\nstorage provider will be made.\n\nThe command will look something like this:\n```bash\n$ pict-rs \\\n    migrate-store \\\n        --concurrency 32 \\\n    filesystem \\\n        -p /path/to/files \\\n    object-storage \\\n        -e https://object-storage-endpoint \\\n        -b bucket-name \\\n        -r region \\\n        -a access-key \\\n        -s secret-key \\\n    sled \\\n        -p /path/to/sled-repo\n```\n\nIf you are running the docker container with default paths, it can be simplified to the following:\n```bash\n$ pict-rs \\\n    migrate-store \\\n    filesystem \\\n    object-storage \\\n        -e https://object-storage-endpoint \\\n        -b bucket-name \\\n        -r region \\\n        -a access-key \\\n        -s secret-key\n```\n\n_This command must be run while pict-rs is offline._\n\nIf you're running with docker-compose, this could look like the following:\n```bash\n$ sudo docker compose stop pictrs # stop the pict-rs container\n$ sudo docker compose run pictrs sh # launch a shell in the pict-rs container\n\u003e pict-rs --version # verify pict-rs version is recent (should probably be 0.4.0 or later)\n\u003e pict-rs \\\n    migrate-store \\\n    filesystem \\\n    object-storage \\\n        -e endpoint \\\n        -b bucket \\\n        -r region \\\n        -a -access-key \\\n        -s secret-key\n\u003e exit\n$ vi docker-compose.yml # edit the docker-compose yaml however you like to edit it, make sure all the variables described below are set\n$ sudo docker compose up -d pictrs # start pict-rs again after the migration. Note that this is not 'docker compose start'. using the `up` subcommand explicitly reloads configurations\n```\ndepending on your version of docker or docker-compose, you might need to use the following command\nto open a shell:\n```bash\n$ sudo docker-compose run -i pictrs sh\n```\n\nHere's an example based on my own object storage that I host myself on kubernetes with\n[`garage`](https://garagehq.deuxfleurs.fr/):\n```bash\n$ pict-rs \\\n    migrate-store \\\n    filesystem \\\n    object-storage \\\n        --use-path-style \\\n        -e http://garage-daemon.garage.svc:3900 \\\n        -b pict-rs \\\n        -r garage \\\n        -a \u003credacted\u003e \\\n        -s \u003credacted\u003e\n```\n\nHere's an example based on a backblaze b2 user's configuration;\n```bash\n$ pict-rs \\\n    migrate-store \\\n    filesystem \\\n    object-storage \\\n        --use-path-style \\\n        -e https://s3.us-east-005.backblazeb2.com \\\n        -r us-east-005 \\\n        -b SitenamePictrs \\\n        -a redacted \\\n        -s redacted\n```\n\nAfter you've completed the migration, update your pict-rs configuration to use object storage. If\nyou configure using environment variables, make sure the following are set:\n- `PICTRS__STORE__TYPE=object_storage`\n- `PICTRS__STORE__ENDPOINT=https://object-storage-endpoint`\n- `PICTRS__STORE__BUCKET_NAME=bucket-name`\n- `PICTRS__STORE__REGION=region`\n- `PICTRS__STORE__USE_PATH_STYLE=false` (set to true if your object storage requires path style access)\n- `PICTRS__STORE__ACCESS_KEY=access-key`\n- `PICTRS__STORE__SECRET_KEY=secret-key`\n\nIf you use the configuration file, this would be\n```toml\n[store]\ntype = \"object_storage\"\nendpoint = \"https://object-storage-endpoint\"\nbucket_name = \"bucket-name\"\nregion = \"region\"\nuse_path_style = false # Set to true if your object storage requires path style access\naccess_key = \"access-key\"\nsecret_key = \"secret-key\"\n```\n\n#### Migration Troubleshooting\nIf you see an error while trying to launch the migration that looks like this:\n```\n   0: IO error: could not acquire lock on \"/mnt/sled-repo/v0.4.0-alpha.1/db\": Os { code: 11, kind: WouldBlock, message: \"Resource temporarily unavailable\" }\n```\nThis means that pict-rs could not open it's database. This is probably because another copy of\npict-rs is currently running. Make sure to stop all running pict-rs processes before migrating.\n\nIf you are trying to migrate and seeing \"Failed moving file. Retrying +1\", Do not cancel the\nmigration. Let it reach 10 retries. It will print a more meaningful error afterwards. Here are some\nexamples of errors and their casuses:\n\nError:\n```\n   0: Error in store\n   1: Error in object store\n   2: Invalid status: 400 Bad Request\n   2: \u003c?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?\u003e\n      \u003cError\u003e\n          \u003cCode\u003eInvalidRequest\u003c/Code\u003e\n          \u003cMessage\u003eAuthorization header's Credential is malformed\u003c/Message\u003e\n      \u003c/Error\u003e\n```\nCause: the region was set improperly. Additionaly a path-style endpoint was used without passing\n`--use-path-style`\n\n\nError:\n```\n   0: Error in store\n   1: Error in object store\n   2: Invalid status: 403 Forbidden\n   2: \u003c?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?\u003e\n      \u003cError\u003e\n          \u003cCode\u003eInvalidAccessKeyId\u003c/Code\u003e\n          \u003cMessage\u003eMalformed Access Key Id\u003c/Message\u003e\n      \u003c/Error\u003e\n```\nCause: the access key was set improperly\n\nIf you have enabled object storage without first migrating your existing files to object storage,\nthese migrate commands may end up retrying file migrations indefinitely. In order to successfully\nresolve this multi-store problem the `--skip-missing-files` flag has been added to the\n`migrate-store` subcommand. This tells pict-rs not to retry migrating a file if that file returns\nsome form of \"not found\" error.\n\n```bash\n$ pict-rs \\\n    migrate-store --skip-missing-files \\\n    filesystem -p /path/to/files \\\n    object-storage \\\n        -e https://object-storage-endpoint \\\n        -b bucket-name \\\n        -r region \\\n        -a access-key \\\n        -s secret-key \\\n    sled \\\n        -p /path/to/sled-repo\n```\n\nIf you have trouble getting pict-rs to upload to your object storage, check a few things:\nDoes your object storage require path-style access? Some object storage providers, like Contabo,\ndon't support virtual hosted buckets. Here's a basic example:\n\nPath style URL: `https://region.example.com/bucket-name`\nVirtual host style URL: `https://bucket-name.region.example.com`\n\nIf you do need to use path style, your command might look like this:\n```\n$ pict-rs \\\n    migrate-store \\\n    filesystem -p /path/to/files \\\n    object-storage \\\n        --use-path-style \\\n        -e https://object-storage-endpoint \\\n        -b bucket-name \\\n        -r region \\\n        -a access-key \\\n        -s secret-key \\\n    sled \\\n        -p /path/to/sled-repo\n```\n\nAdditionally, some providers might require you include the `region` in your endpoint URL:\n`https://region.host.com`, while others might just require a top-level endpoint:\n`https://example.com`.\n\nCheck your object storage provider's documentation to be sure you're setting the right values.\n\n### Sled to Postgres Migration\nIf you upgraded to 0.5 without migrating to postgres at the same time, you can migrate to postgres\nafter the fact with the built-in migration utility. Before running the migration, make sure you have\na postgres role and database ready for pict-rs. The first thing pict-rs will do upon connecting to\na new database is attempt to add the `pgcrypto` extension, so if the role you created for pict-rs\ndoes not have that permission, it will fail.\n\nThe migration command is fairly simple. It just needs the path to your existing repo and the URL to\nyour new repo.\n\n```bash\n$ pict-rs \\\n    migrate-repo \\\n    sled -p /path/to-/sled-repo \\\n    postgres -u postgres://user:password@host:5432/db\n```\n\nIf you're running with docker-compose, this could look like the following:\n```bash\n$ sudo docker compose stop pictrs # stop the pict-rs container\n$ sudo docker compose run pictrs sh # launch a shell in the pict-rs container\n\u003e pict-rs --version # verify pict-rs version is recent (should probably be 0.5.0 or later)\n\u003e pict-rs \\\n    migrate-repo \\\n    sled -p /mnt/sled-repo \\\n    postgres -u postgres://user:password@host:5432/db\n\u003e exit\n$ vi docker-compose.yml # edit the docker-compose yaml however you like to edit it, make sure all the variables described below are set\n$ sudo docker compose up -d pictrs # start pict-rs again after the migration. Note that this is not 'docker compose start'. using the `up` subcommand explicitly reloads configurations\n```\n\n_This command must be run while pict-rs is offline._\n\nThis migration should be pretty quick, since there's no actual files getting moved around. After the\nmigration completes, make sure pict-rs is configured to use the postgres repo, then start it back\nup.\n\nExample:\n```toml\n[repo]\ntype = 'postgres'\nurl = 'postgres://user:password@host:5432/db'\n```\nor\n```\nPICTRS__REPO__TYPE=postgres\nPICTRS__REPO__URL=postgres://user:password@host:5432/db\n```\n\n\n## Development\npict-rs has a few native dependencies that need to be installed in order for it to run properly.\nCurrently these are as follows:\n\n- imagemagick 7.1.1 (although 7.0 and 7.1.0 may also work)\n- ffmpeg 6 (although 5 may also work)\n- exiftool 12.62 (although 12.50 or newer may also work)\n\nAdditionally, pict-rs requires a protobuf compiler during the compilation step to support\ntokio-console, a runtime debug tool.\n\nInstalling these from your favorite package manager should be sufficient. Below are some fun ways to\ndevelop and test a pict-rs binary.\n\n### Nix Development\nI personally use nix for development. The provided [`flake.nix`](./flake.nix) file should be\nsufficient to create a development environment for pict-rs on any linux distribution, provided nix\nis installed.\n\n#### With direnv and nix-direnv\nWith these tools, the pict-rs development environment can be automatically loaded when entering the\npict-rs directory\n\nSetup (only once):\n```\n$ echo 'use flake' \u003e .envrc\n$ direnv allow\n```\n\nRunning:\n```\n$ cargo run -- -c dev.toml run\n```\n#### With just Nix\n```\n$ nix develop\n$ cargo run -- -c dev.toml run\n```\n\n### Docker Development\nPreviously, I have run pict-rs from inside containers that include the correct dependencies. The two\noptions listed below are ones I have personally tried.\n\n#### With Arch\nThis option doesn't take much configuration, just compile the binary and run it from inside the container\n\n```bash\n$ cargo build\n$ sudo docker run --rm -it -p 8080:8080 -v \"$(pwd):/mnt\" archlinux:latest\n\u003e pacman -Syu imagemagick ffmepg perl-image-exiftool\n\u003e PATH=$PATH:/usr/bin/vendor_perl /mnt/target/debug/pict-rs --log-targets debug run\n```\n\n#### With Alpine\nThis option requires `cargo-zigbuild` to be installed. Cargo Zigbuild is a tool that links rust\nbinaries with Zig's linker, enabling easy cross-compiles to many targets. Zig has put a lot of\neffort into seamless cross-compiling, and it is nice to be able to take advantage of that work from\nrust.\n\n```bash\n$ cargo zigbuild --target=x86_64-unknown-linux-musl\n$ sudo docker run --rm -it -p 8080:8080 -v \"$(pwd):/mnt\" alpine:3.18\n\u003e apk add imagemagick ffmpeg exiftool\n\u003e /mnt/target/x86_64-unknown-linux-musl/debug/pict-rs --log-targets debug run\n```\n\n\n## Contributing\nFeel free to open issues for anything you find an issue with. Please note that any contributed code\nwill be licensed under the AGPLv3.\n\n\n## FAQ\n### Question: Is pict-rs stateless\nAnswer: It can be. By default, pict-rs uses on-disk storage for files as well as an on-disk\nkey-value store called `sled` for metadata. This is for ease of deployment for small setups. If you\nneed pict-rs to keep no local state (aside from /tmp), it can be configured to use Object Storage\nfor files and Postgres for metadata.\n\n### Question: Can I use a different database with pict-rs\nAnswer: Yes. pict-rs supports both `sled` and `postgres` for storing metadata. In the future I might\nalso support `BonsaiDB`. If you want pict-rs to support another database, feel free to submit\nchanges :)\n\n### Question: How can I submit changes\nAnswer: If you would like to contribute to pict-rs, you can push your code to a public git host of\nyour choice and let me know you did so via matrix or email. I can pull and merge your changes into\nthis repository from there.\n\nAlternatively, you are welcome to email me a patch that I can apply.\n\nI will not be creating additional accounts on my forgejo server, sorry not sorry.\n\n### Question: I want to configure it with yaml instead of toml\nAnswer: That's not a question, but you can configure pict-rs with json, hjson, yaml, ini, or toml.\nWriting configs in other formats is left as an exercise to the reader.\n\n### Question: How do I donate to pict-rs\nAnswer: You don't. I get paid by having a job where I do other stuff. Don't give me money that I\ndon't need.\n\n## Common Problems\nIn some cases, pict-rs might crash and be unable to start again. The most common reason for this is\nthe filesystem reached 100% and pict-rs could not write to the disk, but this could also happen if\npict-rs is killed at an unfortunate time. If this occurs, the solution is to first get more disk for\nyour server, and then look in the `sled-repo` directory for pict-rs. It's likely that pict-rs\ncreated a zero-sized file called `snap.somehash.generating`. Delete that file and restart pict-rs.\n\nWhen running with the provided docker container, pict-rs might fail to start with an IO error saying\n\"permission denied\". This problably means pict-rs' volume is not owned by the correct user. Changing\nthe ownership on the pict-rs volume to `991:991` should solve this problem.\n\n## License\n\nCopyright © 2022 Riley Trautman\n\npict-rs is free software: you can redistribute it and/or modify it under the terms of the GNU\nGeneral Public License as published by the Free Software Foundation, either version 3 of the\nLicense, or (at your option) any later version.\n\npict-rs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even\nthe implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General\nPublic License for more details. This file is part of pict-rs.\n\nYou should have received a copy of the GNU General Public License along with pict-rs. If not, see\n[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobinst%2Fpict-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobinst%2Fpict-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobinst%2Fpict-rs/lists"}