Redis¶
AppMecca provides a shared, managed Redis cache your application can use for
caching, session storage, and task queues. Enabling it is a single line in
appmecca.yaml, and AppMecca injects the connection URL into your containers
automatically.
This block used to be called cache:
It was renamed to redis: because cache: also names the per-service
deploy[].cache: block, which configures HTTP edge caching and has
nothing to do with Redis. One word for two subsystems caused real
confusion — including inside AppMecca's own engineering — so the three
concerns now have three names:
| Block | Where | What it is |
|---|---|---|
redis: |
config root | your managed Redis datastore (REDIS_URL) |
cdn: |
config root | how far your CDN reach extends |
cache: |
per service | edge caching policy — see Content Caching |
A leftover cache: at the config root is refused with a message naming
the replacement, rather than being silently ignored — ignoring it would
deploy your app without REDIS_URL.
Putting cache: at the wrong nesting level is silently ignored — no error, no
warning, and no Redis.
Enabling the Cache¶
Add this to your appmecca.yaml, at the top level (not inside a service):
enabled: true is required — a bare cache: {} block leaves the cache off.
On the next deployment, AppMecca will inject Redis connection details into all of your containers as environment variables.
Injected Environment Variables¶
When cache.enabled: true, AppMecca injects these 2 environment variables into every container:
| Variable | Example Value | Description |
|---|---|---|
REDIS_URL |
redis://cache-host.internal:6379/0 |
Redis connection URL |
CELERY_BROKER_URL |
redis://cache-host.internal:6379/0 |
Same URL, for Celery compatibility |
Both variables contain the same URL. CELERY_BROKER_URL is provided as a convenience because Celery looks for it by default. Read them from the environment — never hard-code the host.
Shared cache
The Redis instance is shared across all applications and all branch deployments in the same environment. Each app and branch connects to the same Redis server. AppMecca injects a CACHE_PREFIX system variable (e.g., myapp:main: or myapp:feature-payments:) to isolate cache keys between branches. Use this prefix in your application code (see examples below).
Flask Example¶
Caching with Flask-Caching¶
requirements.txt:
app.py:
import os
from flask import Flask
from flask_caching import Cache
app = Flask(__name__)
# Configure Redis cache from APPMECCA-injected REDIS_URL
app.config["CACHE_TYPE"] = "RedisCache"
app.config["CACHE_REDIS_URL"] = os.environ.get("REDIS_URL")
# CACHE_PREFIX isolates keys between apps AND branches (e.g., "myapp:main:")
app.config["CACHE_KEY_PREFIX"] = os.environ.get("CACHE_PREFIX", "")
cache = Cache(app)
@app.route("/")
@cache.cached(timeout=60)
def index():
return "This response is cached for 60 seconds."
Key prefixing
Because the Redis instance is shared across apps and branches, always use the CACHE_PREFIX system variable to prefix your cache keys. This variable includes both the app name and the sanitised branch name (e.g., myapp:feature-payments:), ensuring complete isolation between branches.
Session Storage with Flask-Session¶
import os
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = os.environ.get("REDIS_URL")
app.config["SESSION_KEY_PREFIX"] = f"{os.environ.get('CACHE_PREFIX', '')}session:"
Session(app)
Django Example¶
Caching with django-redis¶
settings/production.py:
import os
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": os.environ["REDIS_URL"],
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
# CACHE_PREFIX isolates keys between apps AND branches
"KEY_PREFIX": os.environ.get("CACHE_PREFIX", ""),
}
}
Session Storage¶
# In settings/production.py, after CACHES config above:
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
Celery Task Queue¶
This is the most common use case for Redis in Django projects: running background tasks with Celery.
appmecca.yaml:
containers:
- name: myapp
dockerfile: Dockerfile
context: .
deploy:
- name: web
container: myapp
port: 8000
command: "gunicorn myapp.wsgi:application -w 4 -b 0.0.0.0:8000"
health_check:
path: /health
environment:
DJANGO_SETTINGS_MODULE: myapp.settings.production
- name: worker
container: myapp
command: "celery -A myapp worker -l info -c 4"
- name: beat
container: myapp
command: "celery -A myapp beat -l info"
database:
enabled: true
redis:
enabled: true
myapp/celery.py:
import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings.production")
app = Celery("myapp")
app.config_from_object("django.conf:settings", namespace="CELERY")
app.autodiscover_tasks()
settings/production.py:
import os
# Celery reads CELERY_BROKER_URL automatically when configured this way
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL")
CELERY_RESULT_BACKEND = os.environ.get("REDIS_URL")
CELERY_ACCEPT_CONTENT = ["json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
Workers don't need a port
Notice that the worker and beat services in the example above have no port field. They are internal services that process tasks from the Redis queue. Only the web service needs a port because it handles HTTP traffic.
Node.js Example¶
Caching with ioredis¶
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);
// CACHE_PREFIX isolates keys between apps AND branches (e.g., "myapp:main:")
const PREFIX = process.env.CACHE_PREFIX || "";
// Set a value
await redis.set(`${PREFIX}user:123`, JSON.stringify(user), "EX", 3600);
// Get a value
const cached = await redis.get(`${PREFIX}user:123`);
Session Storage with Express¶
import session from "express-session";
import RedisStore from "connect-redis";
import Redis from "ioredis";
const redisClient = new Redis(process.env.REDIS_URL);
app.use(
session({
store: new RedisStore({
client: redisClient,
prefix: `${process.env.CACHE_PREFIX || ""}session:`,
}),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
})
);
BullMQ Task Queue¶
import { Queue, Worker } from "bullmq";
import Redis from "ioredis";
const connection = new Redis(process.env.REDIS_URL);
// Create a queue
const emailQueue = new Queue("email", { connection });
// Add a job
await emailQueue.add("send-welcome", { userId: 123 });
// Process jobs (in a worker service)
const worker = new Worker(
"email",
async (job) => {
await sendWelcomeEmail(job.data.userId);
},
{ connection }
);
Using the Redis URL Template¶
If your application expects a different environment variable name for the Redis URL, you can use the ${redis_url} template in your appmecca.yaml:
deploy:
- name: web
container: myapp
port: 8000
environment:
MY_CACHE_URL: "${redis_url}"
QUEUE_BACKEND: "${redis_url}"
redis:
enabled: true
The ${redis_url} template resolves to the same value as the REDIS_URL variable. See Environment Variables for the full list of available templates.
Rotating the Cache Password¶
On an environment where REDIS_URL carries a per-app password (rediss://tnt2-…@…),
mecca app rotate-credentials replaces it — alone with --store cache, or together with
the database password by default:
When it completes ElastiCache refuses the old password, and AppMecca redeploys the
release the branch is running so your containers get the new one. ElastiCache can
take several minutes to apply a password change. If it is still applying when the
command finishes, the cache line reads rotated, still applying: the old password
is refused once ElastiCache finishes, and there is nothing to re-run. Where REDIS_URL
carries no password at all, there is nothing to rotate and the result says
skipped. Rotation needs the ADMINISTRATOR or OWNER role in the app's group
and a signed-in session. See Rotating Credentials
for what each outcome means.
Combining Database and Cache¶
Most production applications need both a database and a cache. Here is a complete example:
containers:
- name: myapp
dockerfile: Dockerfile
context: .
deploy:
- name: web
container: myapp
port: 8000
health_check:
path: /health
- name: worker
container: myapp
command: "celery -A myapp worker -l info"
database:
enabled: true
redis:
enabled: true
With this configuration, every container receives all 8 variables:
| Variable | Source |
|---|---|
DATABASE_URL |
Database |
DB_HOST |
Database |
DB_PORT |
Database |
DB_NAME |
Database |
DB_USER |
Database |
DB_PASSWORD |
Database |
REDIS_URL |
Cache |
CELERY_BROKER_URL |
Cache |
Next Steps¶
- Content Caching -- Edge caching for static assets, SPAs, and frontend services
- Database -- Enable PostgreSQL and run migrations
- Environment Variables -- How all environment variables work, including secrets
- Services -- Ports, health checks, commands, and routing
- Inter-Service Communication -- Connect services to each other