Deploy imgproxy to Google Cloud-Run

and securely deliver images custom to your application in next-gen formats.

Chukwuma Nwaugha
6 min readSep 1, 2022
Photo from https://imgproxy.net/

This post describes creating a custom image service for your application using imgproxy — a fast and secure image processing tool — Google Cloud Run and Google Cloud Storage.

Table of content

  1. Why Imgproxy
  2. Install Imgproxy
  3. Deploy to Google Cloud-Run using Docker
  4. Construct URLs and serve images
  5. Connect to Google Cloud Storage
  6. Define allowed image sources
  7. Conclusion.

Why Imgproxy

Imgproxy is a blazing-fast image processing tool that allows applications to process and deliver images performantly. With this tool, your application images can be hosted anywhere on the internet and delivered to the browser with on-the-fly (OTF) transformations.

Secondly, the most common image formats, JPEG and PNG, used in most applications, are now old. With imgproxy, your application can leverage advances in image compression to deliver optimized images in modern formats, e.g., webp and avif. These new formats, known as next-gen image formats, are free to use, widely supported by modern browsers, and result in a significant reduction in size (between 20% and 50%), making them faster and thus more efficient.

Install Imgproxy

Imgproxy is easy to install and has different ways of doing it. We’ll use Docker to install imgproxy as a standalone application. First, confirm that you have Docker installed on your machine by running the following command on your favorite terminal to output meta-info for available containers and images:

$ docker info

Then pull the latest official image from Docker Hub:

$ docker pull darthsim/imgproxy:latest

Next, run the following command to start imgproxy on port 8080

$ docker run -p 8080:8080 -it darthsim/imgproxy

If everything goes well, visiting localhost:8080 on your browser should give the following:

Deploy to Google Cloud-Run using Docker

Prerequisites:

  1. Enable Cloud-Run API
  2. Enable container registry (https://cloud.google.com/container-registry/docs/pushing-and-pulling)
  3. Install the gcloud CLI and run gcloud auth configure-docker gcr.io followed by gcloud auth configure-docker {region}.gcr.io where the region can be us, eu, asia, etc.

Tag and push the container:

After meeting the prerequisites, run the following commands on the terminal.

$ docker tag darthsim/imgproxy gcr.io/{PROJECT_ID}/imgproxy

$ docker push gcr.io/{PROJECT_ID}/imgproxy

Note that PROJECT_ID is the ID of your project on GCP and is different from the project number.

Create a Cloud-Run service:

Go to the GCP cloud console and create a Cloud-Run service

  1. Choose the option to deploy one revision from an existing container image
  2. In the input box, press the select button floating right of it. A side panel would emerge, showing all the available images in container and artifact registry tabs. Select the container registry tab since that’s where we pushed the image.
  3. Click the dropdown by the image name pushed earlier and select the latest image, which will prefill your service name, which you can change.
  4. Select the configuration options that best meet your needs. For a simple app, I’d choose the following:
    CPU allocation: allocate CPU only when requests are processing
    Autoscaling: Minimum number of instances: 0; Maximum number of instances: 2
    Ingress: Allow all traffic
    Authentication *: allow unauthenticated invocations
    Container, Connections, Security tab: expand and keep default everywhere (you can use custom options fitting your project) except the environment variables section, which we shall get to later.
  5. Environment variables: as a twelve-factor compliant app, we can configure imgproxy using environment variables without needing to modify any file. The following are environment variables needed for the simple setup on cloud-run (you can find more here):
    – IMGPROXY_DOWNLOAD_TIMEOUT: 10
    – IMGPROXY_TTL=31536000
    – IMGPROXY_ENABLE_WEBP_DETECTION=true
    – IMGPROXY_USE_GCS=
    – IMGPROXY_ALLOWED_SOURCES=
    – IMGPROXY_GCS_KEY=
    Note that both numeric values above are in seconds, and there are currently no values for use_gcs, allowed_sources and gcs_key. Also, note that webp_detection is set to true to allow leveraging next-gen image format. If you prefer avif , then set IMGPROXY_ENABLE_AVIF_DETECTION to true — avif_detection takes precedence over webp_detection and also requires more memory.
  6. Create the service, wait for complete initialization and provisioning, and confirm that imgproxy is running by visiting the service URL.

Construct URLs and serve images

With the cloud-run service now running on imgproxy docker container, you can serve any image publicly available on the internet by constructing the URL following a defined pattern.

/%signature/%processing_options/{plain|encoded}/%source_url@%extension

Let’s see what the different parts of the pattern represent:

Signature

A Signature prevents your URLs from being altered by an attacker, and imgproxy allows the signing of URLs with key/salt pairs specified by the following environment variables:

  • IMGPROXY_KEY: hex-encoded key
  • IMGPROXY_SALT: hex-encoded salt
  • IMGPROXY_SIGNATURE_SIZE: number of bytes to use for signature before encoding to Base64. Default: 32

You can specify multiple key/salt pairs by dividing the keys and salts with a comma (,). Imgproxy will check URL signatures with each pair which is useful when you need to change key/salt pairs in your application while incurring zero downtime.

Processing Options

These values are used to instruct imgproxy on transforming and delivering requested images and are specified as URL paths divided by slashes (/), and presented in the following format:

%option_name:%argument1:%argument2:...:argumentNe.g. /crop:0:0:no/rs:fill:64:64:1/...

Source URL

The source URL can be specified using any of the following two approaches:

  1. Plain source: provide the source URL as is, prepended by the /plain/ segment. For example /plain/http://example.com/images/curiosity.jpg
  2. Base64 encoded source: provide the source URL as a URL-safe Base64 encoded string. For example /aHR0cDovL2V4YW1w/bGUuY29tL2ltYWdl/cy9jdXJpb3NpdHku/anBn.png

Extension

Used to specify the format of the resulting image delivered and is determined in the following ways:

  1. Set explicitly using @ext: for example, using the following URL /plain/http://example.com/images/curiosity.png@jpg would have the image delivered in jpg format.
  2. When omitted: imgproxy will use the source image format as the resulting extension and fallback to jpg if the source image format is not supported.
  3. With webp/avif detection enabled: imgproxy defaults to webp or avif if either IMGPROXY_ENABLE_WEBP_DETECTION or IMGPROXY_ENABLE_AVIF_DETECTION environment variables is set. With this, an image with the URL /plain/http://example.com/images/curiosity.png would be delivered in either webp or avif next-gen format.

Connect to Cloud Storage

Now that we can construct URLs and deliver images using imgproxy, let’s tell the service how to fetch images from application-specific storage, e.g., Google Cloud Storage (GCS), Amazon S3, or Azure Blob Storage— we’d use GCS in this post.

To connect imgproxy to GCS, let’s go back to the cloud-run instance on the GCP console and enter values for the following unfilled environment variables from earlier on:

  1. IMGPROXY_USE_GCS = true
  2. IMGPROXY_GCS_KEY: setting this value depends on whether the service account attached to the cloud-run instance has access to the GCS bucket. If yes, ignore this variable, as imgproxy will use the credentials provided by Google. Otherwise, go to GCP IAM and grant the service account the required access to the GCS bucket — Storage Object Viewer permission.

Imgproxy should now be able to process and deliver application images hosted on GCS.

Define allowed image sources

It is essential to provide a layer of security to application services to prevent abuses or malicious attacks. In the case of our image service, the goal is to ensure that imgproxy does not process and deliver just any publicly accessible image by unknown actors. To this end, we will restrict imgproxy to only serve images from application-specific domains, e.g., GCS URIs, meaning that imgproxy would fail to deliver images from sources outside the list of specified domains.

Allowed Sources: a safelist of source image URLs imgproxy is permitted to serve and set using the IMGPROXY_ALLOWED_SOURCES environment variable. Note the following when setting this variable:

  1. Include wildcards with * to match all characters except /.
  2. When blank (the default), imgproxy allows all source image URLs.
  3. Always add a trailing slash after the host, e.g. http://example.com/
Few examples are: s3://,https://*.example.com/,local://,gs://

Conclusion

With the newly set up image service, your apps will benefit from faster and more efficient next-gen image formats. You will also benefit from the many image transformations, such as cropping, resizing, masking, rotating, gravity, blurring, text overlays, watermarks, etc.

References

  1. https://docs.imgproxy.net/
  2. https://www.digitalocean.com/community/tutorials/how-to-serve-next-generation-images-with-imgproxy-using-docker

--

--

Chukwuma Nwaugha

Follower of Christ. JavaScript enthusiast. Everything GCP. Love making stuff. Twitter: @ChukwumaNwaugha