Overview
The Inmobiliaria API supports two storage backends for property images:- Local Filesystem - Store files on the server (development/simple deployments)
- AWS S3 - Store files in cloud object storage (production/scalable deployments)
STORAGE_DRIVER environment variable.
Storage Driver Selection
Set the storage backend in your.env file:
.env
src/services/storage/types.ts
Local Storage
Stores uploaded files on the server’s filesystem.Configuration
Set to
local to use filesystem storage.Directory path where files are stored (relative to project root).Examples:
uploads(default)./public/uploads/var/www/inmobiliaria/uploads
The directory is automatically created if it doesn’t exist.
URL path for accessing uploaded files.Examples:
/uploads(default - relative path)https://yourdomain.com/uploads(absolute URL)
The Express server automatically serves files from this path when using local storage.
Implementation
The local storage driver (src/services/storage/local.ts):
File Structure
Files are organized by property:Static File Serving
The Express server serves local uploads:src/index.ts
- Cache-Control: 7 days
- Public caching enabled
- Suitable for static property images
Pros & Cons
Advantages
Advantages
- Simple setup - No external dependencies
- No additional costs - Uses existing server storage
- Fast local access - No network latency
- Easy debugging - Files visible on filesystem
Disadvantages
Disadvantages
- Not scalable - Limited to single server
- Server disk space - Constrained by server storage
- No CDN - Higher bandwidth costs
- Deployment challenges - Files lost on container restart
- No geographic distribution - Slower for global users
Best For
- Development environments
- Small deployments (< 1000 properties)
- Single-server setups
- Budget-constrained projects
AWS S3 Storage
Stores files in AWS S3 or S3-compatible services (Cloudflare R2, Backblaze B2, MinIO).Configuration
Set to
s3 to use S3 storage.S3 bucket name.Example:
inmobiliaria-uploadsAWS region or
auto for Cloudflare R2.Examples:us-east-1(AWS)eu-west-1(AWS)auto(Cloudflare R2)
AWS access key ID with S3 permissions.Example:
AKIAIOSFODNN7EXAMPLERequired IAM permissions:s3:PutObject- Upload filess3:GetObject- Read filess3:DeleteObject- Delete files
AWS secret access key.Example:
wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEYCustom endpoint for S3-compatible services.Leave empty for AWS S3. Only set for non-AWS services:
- Cloudflare R2:
https://[account-id].r2.cloudflarestorage.com - Backblaze B2:
https://s3.us-west-000.backblazeb2.com - MinIO:
https://minio.yourdomain.com
CDN or public URL for accessing files.If not set: Uses default S3 URL format
https://[bucket].s3.[region].amazonaws.com
- CloudFront:
https://d111111abcdef8.cloudfront.net - Cloudflare R2:
https://pub-xxxxx.r2.dev - Custom domain:
https://cdn.yourdomain.com
Implementation
The S3 storage driver (src/services/storage/s3.ts):
IAM Policy Example
Minimum required permissions:S3 Bucket Configuration
Create Bucket
Create an S3 bucket in your AWS account or S3-compatible service.Recommended settings:
- Block public access: OFF (if serving directly)
- Versioning: Optional
- Encryption: Enabled
Set Bucket Policy (Optional)
For public read access:
Skip this if using CloudFront or requiring authenticated access.
Pros & Cons
Advantages
Advantages
- Scalable - Handles millions of files
- Reliable - 99.999999999% durability (AWS)
- CDN integration - Fast global delivery
- No server storage - Independent of server disk
- Stateless - Works with container deployments
- Cost-effective - Pay only for what you use
Disadvantages
Disadvantages
- External dependency - Requires S3 account
- Network latency - Upload/download over network
- Costs - Storage + transfer fees
- Complex setup - IAM, buckets, CDN configuration
Best For
- Production environments
- Scalable deployments
- Container-based hosting (Docker, Kubernetes)
- Global user base (with CDN)
- High-traffic applications
S3-Compatible Services
Cloudflare R2
Zero egress fees, S3-compatible API..env
- No egress fees
- Global edge network
- Automatic caching
Backblaze B2
Low-cost S3-compatible storage..env
- Very low cost ($5/TB/month)
- Free egress up to 3x storage
- S3-compatible API
MinIO
Self-hosted S3-compatible storage..env
- Self-hosted (full control)
- No external costs
- S3-compatible API
- Open source
Image Processing
All uploaded images are processed into two variants using Sharp:Variants
| Variant | Suffix | Max Dimensions | Quality | Purpose |
|---|---|---|---|---|
| Large | -lg.webp | 1920x1080 | 85% | Property detail pages |
| Thumbnail | -thumb.webp | 400x300 | 80% | Listings, previews |
Processing Pipeline
Storage Keys
Images are stored with predictable keys:Upload Limits
Configured insrc/middleware/uploads.ts:
Maximum file size per image in megabytes.
Maximum number of files per upload request.
File Filter
Only image files are accepted:image/jpegimage/pngimage/webpimage/gif- Any other
image/*type
Migration Guide
Local to S3
Update database URLs (if needed)
If
PUBLIC_UPLOAD_URL_BASE changed, update property_images.image_url:Troubleshooting
Local: Upload directory not writable
Local: Upload directory not writable
Error:
EACCES: permission deniedSolutions:- Check directory permissions:
ls -la uploads/ - Grant write access:
chmod 755 uploads/ - Ensure process user has write permissions
- Use absolute path in
UPLOAD_DIR
Local: Files not accessible
Local: Files not accessible
Error: 404 when accessing image URLsSolutions:
- Verify
PUBLIC_UPLOAD_URL_BASEmatches static route - Check Express static middleware is configured
- Ensure files exist in upload directory
- Check file permissions (readable by server process)
S3: Access Denied
S3: Access Denied
Error:
Access Denied when uploadingSolutions:- Verify IAM permissions include
s3:PutObject - Check bucket name is correct
- Ensure access keys are valid
- Verify bucket policy doesn’t block uploads
S3: Invalid Credentials
S3: Invalid Credentials
Error:
The AWS Access Key Id you provided does not existSolutions:- Verify
S3_ACCESS_KEY_IDandS3_SECRET_ACCESS_KEY - Check credentials haven’t expired
- For IAM roles, ensure role is attached
- For Cloudflare R2, use R2-specific tokens
S3: Endpoint Resolution Failed
S3: Endpoint Resolution Failed
Error:
Could not resolve hostSolutions:- For AWS S3: Remove or unset
S3_ENDPOINT - For R2/B2: Verify endpoint URL is correct
- Check network connectivity
- Ensure region is valid
Images not displaying
Images not displaying
Error: Images upload but don’t show on frontendSolutions:
- Check
PUBLIC_UPLOAD_URL_BASEis accessible - Verify CORS headers for S3 bucket
- Check CDN configuration
- Inspect network tab for 404/403 errors
- Verify image URLs in database match storage
Performance Optimization
CDN Configuration
CDN Configuration
Use a CDN for faster image delivery:CloudFront (AWS):
- Create distribution with S3 origin
- Enable compression
- Set cache TTL (e.g., 1 year for images)
- Configure custom domain
- Enable R2 public bucket
- Automatic global caching
- Free bandwidth
Image Optimization
Image Optimization
Already implemented:
- WebP format (smaller than JPEG/PNG)
- Responsive variants (lg/thumb)
- Quality optimization (85%/80%)
- Enable CDN image transformations
- Use responsive images in frontend (
<picture>,srcset) - Lazy loading for below-fold images
Caching Headers
Caching Headers
Local storage: 7-day cache (configured)S3 storage: Set
Cache-Control in uploads:See Also
- Environment Variables - Storage configuration variables
- Database Configuration - Image metadata storage
- Properties API - Image upload endpoint