Fix: Sharp Not Working — Installation Failing, Image Not Processing, or Build Errors on Deploy
Quick Answer
How to fix Sharp image processing issues — native binary installation, resize and convert operations, Next.js image optimization, Docker setup, serverless deployment, and common platform errors.
The Problem
Sharp fails to install:
npm install sharp
# Error: Could not load the "sharp" module using the linux-x64 runtime
# Or: sharp: Installation error: command failedOr image processing throws at runtime:
import sharp from 'sharp';
await sharp('input.jpg').resize(300, 200).toFile('output.jpg');
// Error: Input file is missing or of an unsupported image formatOr the build works locally but fails in CI/Docker:
Error: Something went wrong installing the "sharp" package
Could not load the "sharp" module using the linuxmusl-x64 runtimeWhy This Happens
Sharp is a high-performance image processing library built on libvips. It uses pre-built native binaries for each platform:
- Sharp downloads platform-specific binaries — during
npm install, Sharp downloads a pre-compiled binary for your OS, architecture, and C library (glibc vs musl). If the download fails or the platform isn’t supported, installation errors occur. - Docker/CI has a different platform than your dev machine — installing Sharp on macOS (darwin-arm64) and deploying to Linux Docker (linux-x64 or linuxmusl-x64) means the local binary won’t work. Dependencies must be installed inside the target environment.
- Sharp can’t process files that don’t exist or are corrupted — paths must be absolute or relative to the working directory. Buffer inputs must contain valid image data. URL inputs aren’t supported directly — fetch first, then pass the buffer.
- Next.js uses Sharp internally —
next/imageoptimization uses Sharp in production. If Sharp isn’t installed or misconfigured, image optimization falls back to a slower implementation or fails.
Fix 1: Correct Installation
# Standard install
npm install sharp
# Force rebuild (if platform changed)
npm rebuild sharp
# Specific platform (for cross-compilation or CI)
npm install --platform=linux --arch=x64 sharp
npm install --platform=linux --arch=arm64 sharp
npm install --platform=linuxmusl --arch=x64 sharp # Alpine Linux
# If behind a corporate proxy
npm install sharp --sharp-libvips-binary-host="https://your-mirror.com"
# Clear cache and reinstall
npm cache clean --force
rm -rf node_modules/sharp
npm install sharpFix 2: Basic Image Operations
import sharp from 'sharp';
// Resize
await sharp('input.jpg')
.resize(800, 600) // width, height
.toFile('output.jpg');
// Resize maintaining aspect ratio
await sharp('input.jpg')
.resize(800) // Width only — height auto-calculated
.toFile('output.jpg');
// Resize with fit options
await sharp('input.jpg')
.resize(800, 600, {
fit: 'cover', // 'cover' | 'contain' | 'fill' | 'inside' | 'outside'
position: 'center', // 'top' | 'right' | 'bottom' | 'left' | 'center' | 'entropy' | 'attention'
})
.toFile('output.jpg');
// Convert format
await sharp('input.png')
.jpeg({ quality: 80, progressive: true })
.toFile('output.jpg');
await sharp('input.jpg')
.webp({ quality: 80, effort: 4 })
.toFile('output.webp');
await sharp('input.jpg')
.avif({ quality: 60 })
.toFile('output.avif');
// From buffer (for API routes / file uploads)
const inputBuffer = await file.arrayBuffer();
const outputBuffer = await sharp(Buffer.from(inputBuffer))
.resize(400, 400, { fit: 'cover' })
.webp({ quality: 80 })
.toBuffer();
// Get image metadata
const metadata = await sharp('input.jpg').metadata();
console.log(metadata.width, metadata.height, metadata.format);
// 4032, 3024, 'jpeg'
// Pipeline — chain operations
await sharp('input.jpg')
.resize(800, 600, { fit: 'cover' })
.blur(2)
.sharpen()
.normalize()
.gamma(1.5)
.webp({ quality: 80 })
.toFile('output.webp');Fix 3: Generate Thumbnails and Responsive Images
import sharp from 'sharp';
import path from 'path';
// Generate multiple sizes for responsive images
async function generateResponsiveImages(inputPath: string, outputDir: string) {
const sizes = [
{ width: 320, suffix: 'sm' },
{ width: 640, suffix: 'md' },
{ width: 1024, suffix: 'lg' },
{ width: 1920, suffix: 'xl' },
];
const filename = path.basename(inputPath, path.extname(inputPath));
const results = await Promise.all(
sizes.map(async ({ width, suffix }) => {
const outputPath = path.join(outputDir, `${filename}-${suffix}.webp`);
await sharp(inputPath)
.resize(width, null, { withoutEnlargement: true })
.webp({ quality: 80 })
.toFile(outputPath);
return { width, path: outputPath };
})
);
return results;
}
// Generate thumbnail with blur placeholder
async function generateThumbnailWithPlaceholder(inputPath: string) {
// Full thumbnail
const thumbnail = await sharp(inputPath)
.resize(300, 300, { fit: 'cover' })
.webp({ quality: 75 })
.toBuffer();
// Tiny blur placeholder (for LQIP — Low Quality Image Placeholder)
const placeholder = await sharp(inputPath)
.resize(10, 10, { fit: 'cover' })
.blur()
.toBuffer();
const placeholderBase64 = `data:image/webp;base64,${placeholder.toString('base64')}`;
return { thumbnail, placeholderBase64 };
}Fix 4: API Route for Image Processing
// app/api/image/route.ts — on-the-fly image processing
import sharp from 'sharp';
import { NextRequest } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl;
const url = searchParams.get('url');
const width = parseInt(searchParams.get('w') || '800');
const height = searchParams.get('h') ? parseInt(searchParams.get('h')!) : undefined;
const quality = parseInt(searchParams.get('q') || '80');
const format = searchParams.get('f') || 'webp';
if (!url) return new Response('Missing url parameter', { status: 400 });
try {
// Fetch the source image
const response = await fetch(url);
if (!response.ok) return new Response('Image not found', { status: 404 });
const buffer = Buffer.from(await response.arrayBuffer());
// Process
let pipeline = sharp(buffer).resize(width, height, {
fit: 'cover',
withoutEnlargement: true,
});
// Output format
switch (format) {
case 'webp': pipeline = pipeline.webp({ quality }); break;
case 'avif': pipeline = pipeline.avif({ quality }); break;
case 'png': pipeline = pipeline.png({ quality }); break;
default: pipeline = pipeline.jpeg({ quality, progressive: true });
}
const output = await pipeline.toBuffer();
return new Response(output, {
headers: {
'Content-Type': `image/${format}`,
'Cache-Control': 'public, max-age=31536000, immutable',
},
});
} catch (error) {
return new Response('Processing failed', { status: 500 });
}
}
// Usage: /api/image?url=https://example.com/photo.jpg&w=400&q=75&f=webpFix 5: Docker Configuration
# Dockerfile — Sharp works in standard Node images
FROM node:20-slim
# Sharp dependencies are pre-installed in node:20-slim
# For Alpine (node:20-alpine), install vips:
# FROM node:20-alpine
# RUN apk add --no-cache vips-dev
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]# Multi-stage build — install dependencies in target platform
FROM node:20-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-slim AS runner
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
CMD ["node", "dist/index.js"]// package.json — ensure Sharp installs for the deploy platform
{
"scripts": {
"postinstall": "npm rebuild sharp"
}
}Fix 6: Watermark and Composite
import sharp from 'sharp';
// Add watermark
async function addWatermark(inputPath: string, watermarkPath: string) {
const watermark = await sharp(watermarkPath)
.resize(200) // Resize watermark
.ensureAlpha(0.5) // Semi-transparent
.toBuffer();
await sharp(inputPath)
.composite([{
input: watermark,
gravity: 'southeast', // Bottom-right corner
blend: 'over',
}])
.toFile('watermarked.jpg');
}
// Add text watermark (using SVG)
async function addTextWatermark(inputPath: string, text: string) {
const metadata = await sharp(inputPath).metadata();
const svg = `
<svg width="${metadata.width}" height="${metadata.height}">
<text
x="50%" y="95%"
text-anchor="middle"
font-family="Arial"
font-size="24"
fill="white"
opacity="0.7"
>${text}</text>
</svg>
`;
await sharp(inputPath)
.composite([{ input: Buffer.from(svg), blend: 'over' }])
.toFile('watermarked.jpg');
}
// Create OG image (combine background + text)
async function createOGImage(title: string) {
const width = 1200;
const height = 630;
// Create background
const background = sharp({
create: { width, height, channels: 4, background: { r: 26, g: 26, b: 46, alpha: 1 } },
});
// Text as SVG
const textSvg = `
<svg width="${width}" height="${height}">
<text x="60" y="${height / 2}" font-family="Arial" font-size="48" font-weight="bold" fill="white">
${escapeXml(title)}
</text>
</svg>
`;
return background
.composite([{ input: Buffer.from(textSvg), blend: 'over' }])
.png()
.toBuffer();
}Still Not Working?
“Could not load the sharp module using the linux-x64 runtime” — Sharp’s native binary doesn’t match the platform. Run npm rebuild sharp in the target environment. In Docker, install dependencies inside the container, not on the host. For serverless (Vercel, Netlify), Sharp is usually pre-installed.
“Input file is missing” — Sharp resolves paths relative to process.cwd(), not the script location. Use absolute paths: path.join(process.cwd(), 'public', 'image.jpg'). For buffers, ensure the buffer contains valid image data, not an HTML error page.
Sharp slows down the build on Vercel/Netlify — Sharp installation downloads a ~25MB native binary. Add sharp to your package.json dependencies (not devDependencies) so it’s available at runtime. Vercel auto-installs Sharp for Next.js projects.
Memory errors on large images — Sharp streams data efficiently, but very large images (50MP+) can exceed memory limits. Use sharp.limitInputPixels(false) to remove the pixel limit, or resize before processing. For batch operations, process images sequentially, not in parallel.
For related image issues, see Fix: Next.js Image Optimization Error and Fix: Blurhash Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Fastify Not Working — 404, Plugin Encapsulation, and Schema Validation Errors
How to fix Fastify issues — route 404 from plugin encapsulation, reply already sent, FST_ERR_VALIDATION, request.body undefined, @fastify/cors, hooks not running, and TypeScript type inference.
Fix: Clack Not Working — Prompts Not Displaying, Spinners Stuck, or Cancel Not Handled
How to fix @clack/prompts issues — interactive CLI prompts, spinners, multi-select, confirm dialogs, grouped tasks, cancellation handling, and building CLI tools with beautiful output.
Fix: pdf-lib Not Working — PDF Not Generating, Fonts Not Embedding, or Pages Blank
How to fix pdf-lib issues — creating PDFs from scratch, modifying existing PDFs, embedding fonts and images, form filling, merging documents, and browser and Node.js usage.
Fix: Pusher Not Working — Events Not Received, Channel Auth Failing, or Connection Dropping
How to fix Pusher real-time issues — client and server setup, channel types, presence channels, authentication endpoints, event binding, connection management, and React integration.