Skip to content

Instantly share code, notes, and snippets.

@bensie
Last active June 24, 2024 12:21
Show Gist options
  • Save bensie/56f51bc33d4a55e2fc9a to your computer and use it in GitHub Desktop.
Save bensie/56f51bc33d4a55e2fc9a to your computer and use it in GitHub Desktop.
ImageMagick Static Binaries for AWS Lambda
#!/usr/bin/env bash
# Must be run on an Amazon Linux AMI that matches AWS Lambda's runtime which can be found at:
# https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
#
# As of May 21, 2019, this is:
# Amazon Linux AMI 2018.03.0 (ami-0756fbca465a59a30)
#
# You need to prepend PATH with the folder containing these binaries in your Lambda function
# to ensure these newer binaries are used.
#
# In a NodeJS runtime, you would add something like the following to the top of
# your Lambda function file:
# process.env['PATH'] = process.env['LAMBDA_TASK_ROOT'] + '/imagemagick/bin:' + process.env['PATH']
#
# This works with both ImageMagick v6.x and v7.x
# version=6.9.10-23
version=7.0.8-45
sudo yum -y install libpng-devel libjpeg-devel libtiff-devel gcc
curl -O https://imagemagick.org/download/ImageMagick-$version.tar.gz
tar zxvf ImageMagick-$version.tar.gz
cd ImageMagick-$version
./configure --prefix=/var/task/imagemagick --enable-shared=no --enable-static=yes
make
sudo make install
tar zcvf ~/imagemagick.tgz /var/task/imagemagick/
@chaddjohnson
Copy link

chaddjohnson commented Apr 11, 2019

It is possible to store symlinks as symlinks in zip files using the --symlinks option with the UNIX zip utility; for example

zip -q -x *.git* --symlinks -r ./deploy-package.zip .

@lefthand
Copy link

lefthand commented May 16, 2019

@leviwilson I remove everything other than convert, identify, and magick in the /bin folder.

@chaddjohnson Unfortunately we don't have control over how Sam zips the files... yet.

@bensie
Copy link
Author

bensie commented May 16, 2019

Gist updated:

  • The Lambda execution environment will invoke functions using Amazon Linux 2018.03 as its base as of May 21, 2019

This script has not yet been tested on the Amazon Linux 2 execution environment used by the nodejs10.x runtime.

@chaddjohnson
Copy link

Has anyone experienced ImageMagick running considerably slower when compiled manually for Lambda?

We tried compiling both ImageMagick 6 and 7, and we also tried the same compilation options that the default ImageMagick install with Lambda uses, but things always run much more slowly.

@mt-sergio
Copy link

mt-sergio commented May 27, 2019

I tried with nodejs10.x and latest Amazon Linux 2 without success.

Command failed: convert
convert: error while loading shared libraries: libtiff.so.5: cannot open shared object file: No such file or directory

When I add the files into the package they go under /opt folder, which is expected and automatically into $PATH (/opt/bin and /opt/lib), but maybe other folder are also critical.

Solution1: Just needed to run ldd /var/task/imagemagick/bin/convert, get that long list down to my computer, and add into the lib/ folder. 🤷‍♂️ .

Just got new one issue:

convert: UnableToOpenConfigureFile 'colors.xml' @ warning/configure.c/GetConfigureOptions/699.

Solution2: Include etc,shared and include imagemagick in serverless folder. Then, these .xml files will be discovered as they will be refereed correctly.

Issue3: I need gs :( I guess I will need to compile with ghostscript...

Why doesn't Imagemagick exist in https://github.com/mthenw/awesome-layers ??

@jp928
Copy link

jp928 commented Jun 1, 2019

In my case, I can't get permission to execute GraphicsMagick from /var/task.
I copied to /tmp and give it permission 755 as mentioned: https://stackoverflow.com/questions/27708573/aws-lambda-making-video-thumbnails/29001078#29001078
Do you mind to share how did you overcome this issue?

@ronaksavla
Copy link

I followed these steps to get Static Binaries for ImageMagic version 6.9.10-5
https://imagemagick.org/download/ImageMagick-6.9.10-55.tar.gz

Seems like the new update has removed convert modules. I confirmed with tech support
https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/

Lambda Code:
`var IM_PATH = "var/task/imagemagick/bin/";
process.env['LD_LIBRARY_PATH'] = 'var/task/imagemagick/lib/';
process.env['PATH'] = process.env['PATH'] + ':' + IM_PATH + ':' + process.env['LD_LIBRARY_PATH'];

var gm = require('gm').subClass({
imageMagick: true,
appPath: 'var/task/imagemagick/bin/',
});

gm('image.png')
.resize(240, 240, '!')
.write('/tmp/resize.png', function (err) {
if (!err) console.log('done');
else {
console.log(err);
}
});
`

Folder structure for Node js 8.10 & Node js 10.x lambdas:
image

Node gm version: "1.23.1"

Here are following errors:

  1. Node js 10.x lambda:
{ Error: Command failed: var/task/imagemagick/bin/convert: error while loading shared libraries: libgomp.so.1: cannot open shared object file: No such file or directory

    at ChildProcess.onExit (/var/task/node_modules/gm/lib/command.js:301:17)
    at ChildProcess.emit (events.js:189:13)
    at ChildProcess.EventEmitter.emit (domain.js:441:20)
    at maybeClose (internal/child_process.js:970:16)
    at Socket.stream.socket.on (internal/child_process.js:389:11)
    at Socket.emit (events.js:189:13)
    at Socket.EventEmitter.emit (domain.js:441:20)
    at Pipe._handle.close (net.js:597:12) code: 127, signal: null }
  1. Node js 8.10 lambda:
{ Error: Command failed: convert: UnableToOpenConfigureFile `delegates.xml' @ warning/configure.c/GetConfigureOptions/677.
convert: NoDecodeDelegateForThisImageFormat `PNG' @ error/constitute.c/ReadImage/560.
convert: NoImagesDefined `/tmp/resize.png' @ error/convert.c/ConvertImageCommand/3235.

    at ChildProcess.onExit (/var/task/node_modules/gm/lib/command.js:301:17)
    at emitTwo (events.js:126:13)
    at ChildProcess.emit (events.js:214:7)
    at maybeClose (internal/child_process.js:925:16)
    at Socket.stream.socket.on (internal/child_process.js:346:11)
    at emitOne (events.js:116:13)
    at Socket.emit (events.js:211:7)
    at Pipe._handle.close [as _onclose] (net.js:567:12) code: 1, signal: null }

@peshkov3
Copy link

Hi @ronaksavla. Have you find the solution?

@chaddjohnson
Copy link

chaddjohnson commented Jul 29, 2019

Here are instructions that worked well for my team and our app. Our app works with large images (e.g., 9000x13000) so it can use a lot of memory. ImageMagick 6 and uses less memory, so we use that.

  1. See https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html. The AMI amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2 was used for these instructions.
  2. On the server, download the latest version of ImageMagick 6 (not 7 as it uses too much memory) from https://imagemagick.org/download/.
  3. Extract the source.
  4. Install dependencies: sudo yum install libpng-devel libjpeg-devel libtiff-devel gcc ImageMagick-devel xz-devel.x86_64 fontconfig-devel.x86_64 libxml2-devel.x86_64 libtool-ltdl-devel.
  5. Run ./configure --prefix=/var/task/imagemagick --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include --libdir=/usr/lib64 --libexecdir=/usr/libexec --localstatedir=/var --sharedstatedir=/var/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared=no --enable-static=yes --with-modules --with-perl=no --with-x=no --with-gslib=no --with-lcms2 --without-rsvg --with-xml --without-dps --without-included-ltdl --disable-hdri --with-quantum-depth=8 --disable-openmp in the source directory.
    1. Note the intentional use of --with-gslib=no. This forces use of the gs binary rather than the system library. See this discussion for details.
  6. Run make in the source directory.
  7. Run sudo make install in the source directory.
  8. Copy only the needed built files from the /var/task/imagemagick/bin directory into a local clone of your repository. If building ImageMagick 7, symlinks are present in the bin directory, so be sure to preserve symlinks using zip --symlinks or tar zcf.
  9. Commit the updated files.

We also had issues working with PDF files. To work around that, we did the following:

  1. In the instructions above, ensure use of --with-gslib=no.
  2. Download the latest version of Ghostscript for Linux x86 (64 bit) from https://www.ghostscript.com/download/gsdnld.html.
  3. Copy the gs-*-linux-x86_64 executable to bin/gs in your repository.
  4. Ensure the bin/gs binary is executable via chmod +x bin/gs.

Then in our Lambda function, above the handler definition, we include the following:

process.env['PATH'] = `${process.env['LAMBDA_TASK_ROOT']}/bin:${process.env['PATH']}`;
process.env['MAGICK_CONFIGURE_PATH'] = '/etc/ImageMagick';

@danhouldsworth
Copy link

This thread has been super helpful - in that I now finally have a working version of IM with PDF support running on Lambda / Node v10.x
However it's also left me some questions :
@ronaksavla - did AWS say why they removed the convert modules? And do we mean the modules like /usr/lib64/ImageMagick-6.7.8/modules-Q16/coders/pdf.la ?
What caught me out during this journey is that Lambda/Node8.10 actually has ImageMagick 6.7.8.9-15.21.amzn1 RPM already installed. (I'm not sure if this was a recent change?) But convert & identify etc work natively out of the box, however it doesn't include PDF support. @bensie - with your script above, I'm pretty sure that won't give you extras such as PDF support, and will rely on the libs / modules included in the Lambda AMI for Node8.10. In which case - why not just use the ImageMagick 6.7.8.9 already in Lambda/Node8.10 ?
@chaddjohnson - thanks for the detailed tips on GS / PDF / gslib config. I found when using Lambda Node10.x I then also had to copy across many of the missing libs & config xmls like delegate.xml. Did you do this also? Or is this using Node8.10 and using the preinstalled IM libs?

@chaddjohnson
Copy link

chaddjohnson commented Aug 2, 2019

@danhouldsworth Glad that was helpful. I am still running this stuff on 8.10 — haven’t tried it in 10.x and don’t plan to for a while. I’m using preinstalled stuff except for the convert binary I compiled and the gs bin I added.

@adnascent
Copy link

@chaddjohnson Thank you for sharing!
@danhouldsworth I had the copy the libraries over as well.
@jp928 I had to do the same thing, copy all the files in the tmp directory and chmod them in code. Then set PATH. Im not sure what everyone else is doing.

For the missing libraries I had to run the command "ldd" on the binaries to see what libraries were missing. EX:
ldd convert

@corbfon
Copy link

corbfon commented Aug 17, 2019

@adnascent the ldd convert command ended up missing some libraries for me, such as libxml2.so.2 which is not listed as a shared lib for either convert or gs. Were you also checking shared libs for any other library?

I've been trying to get this working over the past couple of weeks on node 10 (also tried downgrading to node 8 and got the same errors as @ronaksavla). I'm considering switching gears and trying this with Lambda Layers. Has anyone tried with Layers before?

@corbfon
Copy link

corbfon commented Aug 21, 2019

I've had success using layers like this on node10.x - https://github.com/serverlesspub/imagemagick-aws-lambda-2 - it worked out of the box, and then with some custom modifications. Going to approach ghostscript the same way.

@chaddjohnson
Copy link

Updated instructions for nodejs10.x runtime

I was able to get this working. This should also work for nodejs12.x.

ImageMagick

Before proceeding, set up a Lambda Execution Environment. See https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html. The AMI amzn2-ami-hvm-2.0.20190313-x86_64-gp2 was used for this compilation.

  1. On the server, download the latest version of ImageMagick 6 or 7 from https://imagemagick.org/download/.
  2. Extract the source.
  3. Install dependencies: sudo yum install libpng-devel libjpeg-devel libtiff-devel gcc ImageMagick-devel xz-devel.x86_64 fontconfig-devel.x86_64 libxml2-devel.x86_64 libtool-ltdl-devel lcms2-devel.
  4. Run ./configure --prefix=/var/task/imagemagick --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include --libdir=/usr/lib64 --libexecdir=/usr/libexec --localstatedir=/var --sharedstatedir=/var/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared=no --enable-static=yes --with-modules --with-perl=no --with-x=no --with-gslib=no --with-lcms --without-rsvg --with-xml --without-dps --disable-hdri --with-quantum-depth=8 --disable-openmp in the source directory.
    1. Note the intentional use of --with-gslib=no. This forces use of the gs binary rather than the system library. See this discussion for details.
  5. Run make in the source directory.
  6. Run sudo make install in the source directory.
  7. Copy only the needed built files from the /var/task/imagemagick/bin directory into a local clone of your repository.
    1. If building ImageMagick 7, symlinks are present in the bin directory, so be sure to preserve symlinks using zip --symlinks or tar zcf.
  8. Copy libbz2.so.1, libexpat.so.1, libfontconfig.so.1, libfreetype.so.6, libgs.so.9, libjbig.so.2.0, libjpeg.so.62, liblcms2.so.2, liblzma.so.5, libpng15.so.15, libtiff.so.5, and libxml2.so.2 to lib/ in your repository from the server in /usr/lib64/. Be sure to copy the files that these symlink to, not the symlinks.

Then in the Lambda function, above the handler definition, include the following:

process.env['PATH'] = `${process.env['LAMBDA_TASK_ROOT']}/bin:${process.env['PATH']}`;
process.env['MAGICK_CONFIGURE_PATH'] = `${process.env['LAMBDA_TASK_ROOT']}/etc/ImageMagick`;

I also copied /etc/ImageMagick from the ami instance into my repository and am setting MAGICK_CONFIGURE_PATH -- I don't know if this is necessary:

process.env['MAGICK_CONFIGURE_PATH'] = `${process.env['LAMBDA_TASK_ROOT']}/etc/ImageMagick`;

Ghostscript (for PDF support)

  1. Download the latest version of Ghostscript for Linux x86 (64 bit) from https://www.ghostscript.com/download/gsdnld.html.
  2. Copy the gs-*-linux-x86_64 executable to bin/gs in your repository.
  3. Ensure the bin/gs binary is executable via chmod +x bin/gs.
  4. Copy libgs.so.9 to lib/ from the server in /usr/lib64/. Be sure to copy the file that this symlinks to, not the symlink.

@singhadarsh93
Copy link

singhadarsh93 commented Jan 23, 2020

@chaddjohnson can you please write command for step 7 and 8 I am very new to linux , it will help , I am just stuck in this,
also there is no libexpat library in usr/lib64

@chaddjohnson
Copy link

chaddjohnson commented Jan 23, 2020

@singhadarsh93 Should be something like this:

  1. Run approximately these commands:

    On the server:

    cd /var/task/imagemagick
    tar zcf bin.tar.gz bin/convert bin/composite bin/identify
    

    On your computer (replace xxx.xxx.xxx.xxx with the server IP):

    ssh -i key.pem ec2-user@xxx.xxx.xxx.xxx 'cat /var/task/imagemagick/bin.tar.gz' > bin.tar.gz
    tar zxf bin.tar.gz
    

    Then there should be a bin directory on your local machine with the binaries.

  2. Run approximately these commands:

    On the server:

    mkdir /home/ec2-user/lib
    cd /usr/lib64
    cp -L libbz2.so.1 libexpat.so.1 libfontconfig.so.1 libfreetype.so.6 libgs.so.9 libjbig.so.2.0 libjpeg.so.62 liblcms2.so.2 liblzma.so.5 libpng15.so.15 libtiff.so.5 and libxml2.so.2 /home/ec2-user/lib/
    cd /home/ec2-user
    tar zcf lib.tar.gz lib/
    

    On your computer:

    ssh -i key.pem ec2-user@xxx.xxx.xxx.xxx 'cat lib.tar.gz' > lib.tar.gz
    tar zxf lib.tar.gz
    

    Then there should be a lib directory on your local machine with the .so files.

Not sure why libexpat isn't present. Maybe try yum install expat-devel. If that doesn't work, try searching via yum search expat, and install the expat devel 64-bit package.

@RahulChouhan96
Copy link

   var IM_PATH = process.env['LAMBDA_TASK_ROOT'] + "/imagemagick/bin/";
   process.env['LD_LIBRARY_PATH'] = '/var/task/imagemagick/lib/';
   process.env['PATH'] = process.env['PATH'] + ':' + IM_PATH;
   var gm = require('gm').subClass({
     imageMagick: true,
    appPath: '/var/task/imagemagick/bin/',
  });

remember to put the imagemagick floder in the root folder

I tried this after installing imagemagick in AWS lambda, but it automatically appends the path with "identify". And I dont know, where is that written.

@dfloresgonz
Copy link

could you share the final stack of files?

@samkit-jain
Copy link

samkit-jain commented Feb 2, 2021

Instructions for Python 3.8

My use-case was to convert pages in a PDF to PNG via wand. I followed the instructions provided by @chaddjohnson at https://gist.github.com/bensie/56f51bc33d4a55e2fc9a#gistcomment-3133859 but was getting errors when using wand. Following are the instructions on how I was able to resolve those by doing some slight adjustments:

  1. Start an EC2 instance and SSH into it. I used the AMI amzn2-ami-hvm-2.0.20210126.0-x86_64-gp2.
  2. Download ImageMagick 6.9.11.
    wget https://download.imagemagick.org/ImageMagick/download/ImageMagick-6.9.11-60.tar.gz
  3. Extract the folder.
    tar zxvf ImageMagick-6.9.11-60.tar.gz
  4. cd into the extracted folder.
    cd ImageMagick-6.9.11-60
  5. Edit the policy.xml file to allow PDF to PNG conversion.
    nano config/policy.xml
    I copy-pasted the following content but you can modify it as needed.
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE policymap [
    <!ELEMENT policymap (policy)+>
    <!ELEMENT policy (#PCDATA)>
    <!ATTLIST policy domain (delegate|coder|filter|path|resource) #IMPLIED>
    <!ATTLIST policy name CDATA #IMPLIED>
    <!ATTLIST policy rights CDATA #IMPLIED>
    <!ATTLIST policy pattern CDATA #IMPLIED>
    <!ATTLIST policy value CDATA #IMPLIED>
    ]>
    <!--
      Configure ImageMagick policies.
    
      Domains include system, delegate, coder, filter, path, or resource.
    
      Rights include none, read, write, and execute.  Use | to combine them,
      for example: "read | write" to permit read from, or write to, a path.
    
      Use a glob expression as a pattern.
    
      Suppose we do not want users to process MPEG video images:
    
        <policy domain="delegate" rights="none" pattern="mpeg:decode" />
    
      Here we do not want users reading images from HTTP:
    
        <policy domain="coder" rights="none" pattern="HTTP" />
    
      Lets prevent users from executing any image filters:
    
        <policy domain="filter" rights="none" pattern="*" />
    
      The /repository file system is restricted to read only.  We use a glob
      expression to match all paths that start with /repository:
      
        <policy domain="path" rights="read" pattern="/repository/*" />
    
      Let's prevent possible exploits by removing the right to use indirect reads.
    
        <policy domain="path" rights="none" pattern="@*" />
    
      Any large image is cached to disk rather than memory:
    
        <policy domain="resource" name="area" value="1GB"/>
    
      Define arguments for the memory, map, area, width, height, and disk resources
      with SI prefixes (.e.g 100MB).  In addition, resource policies are maximums
      for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
      exceeds policy maximum so memory limit is 1GB).
    -->
    <policymap>
      <!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
      <policy domain="resource" name="memory" value="256MiB"/>
      <policy domain="resource" name="map" value="512MiB"/>
      <policy domain="resource" name="width" value="16KP"/>
      <policy domain="resource" name="height" value="16KP"/>
      <policy domain="resource" name="area" value="128MB"/>
      <policy domain="resource" name="disk" value="1GiB"/>
      <!-- <policy domain="resource" name="file" value="768"/> -->
      <!-- <policy domain="resource" name="thread" value="4"/> -->
      <!-- <policy domain="resource" name="throttle" value="0"/> -->
      <!-- <policy domain="resource" name="time" value="3600"/> -->
      <!-- <policy domain="system" name="precision" value="6"/> -->
      <!-- not needed due to the need to use explicitly by mvg: -->
      <!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
      <!-- use curl -->
      <policy domain="delegate" rights="none" pattern="URL" />
      <policy domain="delegate" rights="none" pattern="HTTPS" />
      <policy domain="delegate" rights="none" pattern="HTTP" />
      <!-- in order to avoid to get image with password text -->
      <policy domain="path" rights="none" pattern="@*"/>
      <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/>
      <!-- disable ghostscript format types -->
      <policy domain="coder" rights="none" pattern="PS" />
      <policy domain="coder" rights="none" pattern="EPI" />
      <policy domain="coder" rights="read|write" pattern="PDF" />
      <policy domain="coder" rights="none" pattern="XPS" />
      <policy domain="coder" rights="read|write" pattern="LABEL" />
    </policymap>
  6. Configure and install ImageMagick.
    ./configure --prefix=/var/task/imagemagick --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include --libdir=/usr/lib64 --libexecdir=/usr/libexec --localstatedir=/var --sharedstatedir=/var/lib --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared=no --enable-static=yes --with-modules --with-perl=no --with-x=no --with-gslib=no --with-lcms --without-rsvg --with-xml --without-dps --disable-hdri --with-quantum-depth=8 --disable-openmp
    make
    sudo make install
  7. Copy the required .so files.
    mkdir lib
    cd /usr/lib64/
    cp -L libbz2.so.1 libexpat.so.1 libfontconfig.so.1 libfreetype.so.6 libgs.so.9 libjbig.so.2.0 libjpeg.so.62 liblcms2.so.2 liblzma.so.5 libpng15.so.15 libtiff.so.5 libxml2.so.2 libMagickCore-6.Q16.so.6 libMagickWand-6.Q16.so.6 libXext.so.6 libXt.so.6 libltdl.so.7 libSM.so.6 libICE.so.6 libX11.so.6 libgomp.so.1 libuuid.so.1 libxcb.so.1 libXau.so.6 libMagickCore-6.Q8.so.6 libMagickWand-6.Q8.so.6 libm.so.6 libz.so.1 libjasper.so.1 /home/ec2-user/lib/
    cp -r ImageMagick-6.9.10/ ImageMagick-6.9.11/ /home/ec2-user/lib/
    cd /home/ec2-user
    tar zcf lib.tar.gz lib/
    Copy the lib.tar.gz file from the server to your local machine.
  8. Copy the required binary files.
    cd /var/task/imagemagick
    sudo tar zcf bin.tar.gz bin/
    cp bin.tar.gz /home/ec2-user/bin.tar.gz 
    Copy the bin.tar.gz file from the server to your local machine.
  9. Copy the XML files required by ImageMagick.
    cd /etc/
    sudo tar zcf etc.tar.gz ImageMagick-6/
    cp etc.tar.gz /home/ec2-user/etc.tar.gz
    Copy the etc.tar.gz file from the server to your local machine.
  10. Close the SSH session.
  11. On your local machine, extract the contents of the 3 *.tar.gz files.
  12. Download ghostscript from https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs9533/ghostscript-9.53.3-linux-x86_64.tgz and extract the ghostscript binary into the bin/ folder and rename it to gs. Run chmod +x bin/gs to make it executable.
  13. Compress the 3 - lib, bin and etc - folders into a ZIP file. The tree structure of the ZIP file would look like
    file.zip/
    |-- bin
    |   |-- convert
    |   |-- ...
    |   `-- gs
    |-- etc
    |   `-- ImageMagick-6
    |       |-- coder.xml
    |       |-- ...
    |       `-- type.xml
    `-- lib
        |-- ImageMagick-6.9.10
        |   |-- config-Q16
        |   |   `-- configure.xml
        |   `-- modules-Q16
        |       |-- coders
        |       |   |-- aai.la
        |       |   |-- ...
        |       |   `-- yuv.so
        |       `-- filters
        |           |-- analyze.la
        |           `-- analyze.so
        |-- ImageMagick-6.9.11
        |   |-- config-Q8
        |   |   `-- configure.xml
        |   `-- modules-Q8
        |       |-- coders
        |       |   |-- aai.la
        |       |   |-- ...
        |       |   `-- yuv.so
        |       `-- filters
        |           |-- analyze.la
        |           `-- analyze.so
        |-- libICE.so.6
        |-- ...
        `-- libz.so.1
    
    I have used ... wherever the folder contained more than 2 files to denote that there are more files present.
  14. Create a Python 3.8 runtime compatible layer on AWS Lambda and use the ZIP created in step 13.
  15. Add the layer to your AWS Lambda function code.
  16. Update environment variables in your lambda function.
    import os
    os.environ["PATH"] = f"/opt/bin:{os.environ['PATH']}"
    os.environ["LD_LIBRARY_PATH"] = f"/opt/lib:{os.environ['LD_LIBRARY_PATH']}"
    os.environ["MAGICK_HOME"] = "/opt/"
    os.environ["WAND_MAGICK_LIBRARY_SUFFIX"] = "-6.Q8"
    os.environ["MAGICK_CONFIGURE_PATH"] = "/opt/etc/ImageMagick-6/"
    os.environ["MAGICK_CODER_MODULE_PATH"] = "/opt/lib/ImageMagick-6.9.11/modules-Q8/coders/"

Note: If the size of the uncompressed ZIP file is too large and you reach AWS Lambda size limits, remove the binaries that you don't need from the bin/ folder. In my case, I only kept Magick-config, MagickCore-config, MagickWand-config, Wand-config, convert and gs and removed others.

NOTE: I also have a ready-to-use ZIP file uploaded at https://github.com/samkit-jain/aws-lambda-imagemagick-ghostscript

@R-Harishkumar
Copy link

R-Harishkumar commented Apr 21, 2021

Where to Place this Code ?

  1. Update environment variables in your lambda function.
import os
os.environ["PATH"] = f"/opt/bin:{os.environ['PATH']}"
os.environ["LD_LIBRARY_PATH"] = f"/opt/lib:{os.environ['LD_LIBRARY_PATH']}"
os.environ["MAGICK_HOME"] = "/opt/"
os.environ["WAND_MAGICK_LIBRARY_SUFFIX"] = "-6.Q8"
os.environ["MAGICK_CONFIGURE_PATH"] = "/opt/etc/ImageMagick-6/"
os.environ["MAGICK_CODER_MODULE_PATH"] = "/opt/lib/ImageMagick-6.9.11/modules-Q8/coders/"

@samkit-jain
Copy link

@R-Harishkumar You may place the code at the top of the Python file.

@ClickheadZ
Copy link

@samkit-jain

I created the layer with your ZIP file and added it to my lambda, along with the code from step 16,
but when I try to do: from wand.image import Image
I get this error: Unable to import module 'lambda_function': No module named 'wand'

And if I try to package wand myself and add it then I get the error where wand doesn't find the shared library for ImageMagick.

Is there any other step you took to get Wand working in AWS Lambda?

@samkit-jain
Copy link

Hi @ClickheadZ Adding the ZIP file as a layer would give you the required libraries that you want at the OS level to communicate with imagemagick, gs, ... You still need the pip package files in your lambda archive. Did you add Wand as a dependency in your lambda function archive? Example here.

@ClickheadZ
Copy link

ClickheadZ commented Feb 24, 2022

@samkit-jain Thanks for the response! I had not added it properly, I repackaged and deployed a zip now and I'm getting a different error, not sure what I did wrong.

Here are the steps I took to package Wand:

  • Connect to an EC2 instance
  • pip install --target=package wand, pip install --target=package magickwand
  • scp the package to my computer, and zip the package along with my lambda function as per AWS zip deployment instructions

In lambda console, my lambda structure now looks like this:

myLambdaFunction
      - magickwand
      - magickwand-0.2.dist-info
      - wand
      - Wand-0.6.7.dist-info
      lambda_function.py

And when i try from wand.image import Image I now get the error message : Unable to import module 'lambda_function': MagickWand shared library not found.\nYou probably had not installed ImageMagick library despite the fact that I have deployed the imagemagick/ghostscript zip as a layer for this lambda and added it. Did I do something wrong in the Wand packaging or is the problem my lambda layer?

@divanshu-b
Copy link

@ClickheadZ Facing same issue, were you able to find a fix?

@blackbinbinbinbin
Copy link

Thanks~ It's work for me !!! But I found a small problem while using it:
curl download url: curl -O https://imagemagick.org/download/ImageMagick-$version.tar.gz , it will return 404 http status...
the ture url is: https://download.imagemagick.org/archive/ImageMagick-$version.tar.xz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment