A self-hosted placeholder image server inspired by picsum.photos.
Serve random images with on-the-fly resizing, filtering, and formatting.
Live: placepix.net · Guide: placepix.net/guide · Made by RIADVICE
- Features
- Get Started in 5 Steps
- Deploy to a Server
- URL Examples
- Developer Guide — full API reference & feature documentation
- Environment Variables
- API
- Development
- License
- On-the-fly resizing —
/300/200withcrop,cover,scale,contain, orsmart(OpenCV face detection) - Filters & effects — grayscale, blur, sepia, noise, pixelate, tint, brightness, contrast, saturation
- Multiple formats — JPEG, PNG, WebP, AVIF
- Solid color placeholders —
/solid/500/300/ff0000with optional text overlay - Aspect ratio sizing —
/ratio/16:9/1080 - Preset dimensions — Instagram, YouTube, Facebook, and more
- Orientation filtering —
?orientation=landscape|portrait|squarishto filter by native aspect ratio - Watermark overlay — image or text with positioning and opacity
- Srcset generation — responsive image sets via
/api/srcset - Device & browser mockups —
/mockup/iphone/600frames a placeholder in a phone, tablet, laptop or browser - Wireframe placeholders —
/skeleton/card/400/300draws lo-fi grey-box / skeleton-loading blocks - Photos in a harmonious colour —
/color/green/800/600?harmony=complementaryreturns a photo in a colour that goes with green instead of a green one (harmonies computed with ColorAide) - Pattern placeholders —
/pattern/stripes/800/600tiles stripes, diagonal lines, a checkerboard, dots or a grid in any two colours - Blurhash previews —
/blurhash/800/600?hash=…renders a blurhash the way a browser shows it while the image loads - Text-contrast report —
/api/contrast/{id}grades overlaid text per region and suggests a?scrim=opacity - Upload API — add images via
POST /api/upload - Admin dashboard — password-protected usage metrics
- Browser caching — ETag, Last-Modified, 304 responses
- S3 storage — optional S3-compatible backend for images
- AI generation (experimental) — OVHcloud AI Endpoints integration
-
Get the image
docker pull riadvice/placepix:latest
-
Add your images
mkdir -p images/nature images/people cp ~/photos/*.jpg images/nature/
-
Run the server
docker run -d -p 3000:3000 -v $(pwd)/images:/app/images --name placepix riadvice/placepix:latestOr use
docker compose up -d. SetPORTto listen somewhere other than 3000. -
Open the UI Visit
http://localhost:3000to browse and build URLs. -
Use it in your HTML
<img src="http://localhost:3000/300/200/nature" />
One command provisions a host: Docker, the deployment directory, and the container pulled from Docker Hub.
curl -fsSL https://raw.githubusercontent.com/riadvice/placepix/master/deploy/provision.sh | bashIt leaves the server holding configuration and state only — no source code, no virtualenv, no build:
/opt/placepix/
├── .env settings and secrets
├── docker-compose.yml
├── data/ metrics database
└── images/ the image library
Deploying a new version is then a pull, not a build:
cd /opt/placepix && docker compose up -dImages are built and published by CI on every version tag, so servers never compile anything. See deploy/README.md for nginx and TLS setup, rollbacks, backups, and a host-migration runbook.
GET /300/200 # Random image, 300x200
GET /300/200.webp # WebP format
GET /300/200/nature # From "nature" category
GET /300/200?grayscale=true # With filter
GET /300/200?text=Hello # With text overlay
GET /300/200?orientation=landscape # Filter by native aspect ratio
GET /ratio/16:9/1080 # Aspect ratio sizing
GET /preset/instagram-square # Preset dimensions
GET /solid/500/300/ff0000 # Solid color placeholder
GET /mockup/iphone/600 # Placeholder inside a phone frame
GET /skeleton/card/400/300 # Lo-fi wireframe block
GET /color/green/800/600?harmony=complementary # Photo in green's complementary colour
GET /pattern/dots/800/600?fg=1e293b&bg=f8fafc # Dotted pattern placeholder
GET /blurhash/800/600?hash=LEHV6nWB2yk8pyo0adR%2A.7kCMdnj # Render a blurhash
GET /300/200?text=Hi&scrim=bottom:0.6 # Gradient scrim behind overlay text
Full reference: Visit /url-builder on your running instance for an interactive explorer.
Copy .env.example to .env and set what you need:
| Variable | Description | Default |
|---|---|---|
HOST |
Bind address | 127.0.0.1:3000 |
PORT |
Port to listen on; overrides the port in HOST |
3000 |
IMAGES_DIR |
Images folder | ./images |
CACHE |
Enable file cache | true |
CDN |
CDN base URL | — |
MAX_WIDTH / MAX_HEIGHT |
Size limits | 2000 |
UPLOAD_ENABLED |
Allow API uploads | true |
WATERMARK_ENABLED |
Show watermark | false |
WATERMARK_TEXT / WATERMARK_IMAGE |
Watermark content | — |
WATERMARK_POSITION |
top-left, bottom-right, etc. |
bottom-right |
GA_TRACKING_ID |
Google Analytics ID | — |
PRIVACY_POLICY_URL |
Legal link | — |
S3_ENABLED |
Use S3-compatible storage | false |
AI_GENERATION_ENABLED |
Auto-generate images via AI | false |
ORIENTATION_SQUARISH_TOLERANCE |
Tolerance for squarish filter (0.15 = 15%) | 0.15 |
Deployment-only settings, read by deploy/docker-compose.yml:
| Variable | Description | Default |
|---|---|---|
PLACEPIX_TAG |
Image tag to run; pin it to roll back | latest |
BIND_ADDR |
Address the port is published on | 127.0.0.1 |
MEMORY_LIMIT |
Container memory ceiling | 1g |
| Endpoint | Description |
|---|---|
GET /api/categories |
List categories |
GET /api/images |
List all images |
GET /api/info/id/{id} |
Image metadata |
GET /api/srcset?id={id}&sizes=... |
Responsive srcset JSON |
GET /api/blurhash/{id} |
Blurhash of an image, with a preview_url |
GET /api/patterns |
List pattern placeholders |
POST /api/upload |
Upload an image |
pip install -e ".[dev]"
pytestRequires Python 3.12+. On Linux you may need OpenCV system libs:
sudo apt-get install libgl1 libglib2.0-0 libsm6 libxext6 libxrender-dev libgomp1MIT — see LICENSE.