Deploy imgproxy to Google Cloud-Run
and securely deliver images custom to your application in next-gen formats.
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
- Why Imgproxy
- Install Imgproxy
- Deploy to Google Cloud-Run using Docker
- Construct URLs and serve images
- Connect to Google Cloud Storage
- Define allowed image sources
- 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:
- Enable Cloud-Run API
- Enable container registry (https://cloud.google.com/container-registry/docs/pushing-and-pulling)
- Install the gcloud CLI and run
gcloud auth configure-docker gcr.io
followed bygcloud auth configure-docker {region}.gcr.io
where the region can beus
,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
- Choose the option to deploy one revision from an existing container image
- 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. - Click the dropdown by the image name pushed earlier and select the
latest
image, which will prefill your service name, which you can change. - 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. - 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 foruse_gcs
,allowed_sources
andgcs_key
. Also, note thatwebp_detection
is set to true to allow leveraging next-gen image format. If you preferavif
, then setIMGPROXY_ENABLE_AVIF_DETECTION
to true —avif_detection
takes precedence overwebp_detection
and also requires more memory. - 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:
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 keyIMGPROXY_SALT
: hex-encoded saltIMGPROXY_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.
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/...
The source URL can be specified using any of the following two approaches:
- Plain source: provide the source URL as is, prepended by the
/plain/
segment. For example/plain/http://example.com/images/curiosity.jpg
- Base64 encoded source: provide the source URL as a URL-safe Base64 encoded string. For example
/aHR0cDovL2V4YW1w/bGUuY29tL2ltYWdl/cy9jdXJpb3NpdHku/anBn.png
Used to specify the format of the resulting image delivered and is determined in the following ways:
- Set explicitly using @ext: for example, using the following URL
/plain/http://example.com/images/curiosity.png@jpg
would have the image delivered injpg
format. - 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. - With webp/avif detection enabled: imgproxy defaults to
webp
oravif
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 eitherwebp
oravif
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:
- IMGPROXY_USE_GCS = true
- 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:
- Include wildcards with
*
to match all characters except/
. - When blank (the default), imgproxy allows all source image URLs.
- 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.