Skip to content

Commit 51ac989

Browse files
authored
Using local and s3 storage in docker compose setup #1026 (#1030)
* update compose file, Dockerfiles, disk.ts * remove debugging console log * remove default profile * Update comments on disk.ts * update AWS config error message (fix linter issue) * add comments to Dockerfiles, update storage configuration docs
1 parent 32a3d3b commit 51ac989

File tree

7 files changed

+107
-14
lines changed

7 files changed

+107
-14
lines changed

.env.example

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,13 @@ SENTRY_PROJECT=
5757
DRIVE_DISK=local # options: local, s3
5858

5959
# Paths for local storage (optional)
60-
FILE_PUBLIC_PATH= # e.g files
61-
FILES_STORAGE_PATH= # e.g /app/storage/files
62-
PUBLIC_FILES_STORAGE_PATH= # e.g /app/storage/public/files (IMPORTANT: has to be in nextjs's public folder)
60+
FILE_STORAGE_ROOT_PATH="/app/storage/files" # e.g /app/storage/files
61+
FILE_PUBLIC_PATH="files" # e.g files
62+
FILES_STORAGE_PATH="/app/storage/files" # e.g /app/storage/files
63+
PUBLIC_FILES_STORAGE_PATH="/app/apps/web/public/files" # e.g /app/storage/public/files (IMPORTANT: has to be in nextjs's public folder)
6364

6465
# AWS S3 (optional)
66+
# If you are using AWS IAM roles, you can skip AWS_ACCESS_KEY and AWS_ACCESS_SECRET
6567
ASW_REGION=
6668
AWS_ACCESS_KEY=
6769
AWS_ACCESS_SECRET=

apps/gateway/docker/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ RUN apk add --no-cache \
8686

8787
RUN addgroup --system --gid 1001 nodejs
8888
RUN adduser --system --uid 1001 latitude
89+
# Set permissions for local storage
90+
# User ID and Group ID 1001 is used to match the user 'latitude' and group 'nodejs' in the builder image.
91+
RUN set -e; \
92+
mkdir -p /app/storage/files; \
93+
mkdir -p /app/apps/web/public/files; \
94+
chown -R 1001:1001 /app/storage/files /app/apps/web/public/files
8995
USER latitude
9096

9197
WORKDIR /app

apps/web/docker/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ COPY --from=builder --chown=nodejs:nextjs /app/apps/web/.next/static ./apps/web/
156156
# Copy all node_modules to ensure dependencies are available
157157
COPY --from=builder --chown=nodejs:nextjs /app/node_modules ./node_modules
158158
COPY --from=builder --chown=nodejs:nextjs /app/apps/web/node_modules ./apps/web/node_modules
159+
# Set permissions for local storage
160+
# User ID and Group ID 1001 is used to match the user 'nextjs' and group 'nodejs' in the runner image.
161+
RUN set -e; \
162+
mkdir -p /app/storage/files; \
163+
mkdir -p /app/apps/web/public/files; \
164+
chown -R 1001:1001 /app/storage/files /app/apps/web/public/files
159165

160166
USER nextjs
161167

apps/workers/docker/Dockerfile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ RUN apk add --no-cache \
7474

7575
RUN addgroup --system --gid 1001 nodejs
7676
RUN adduser --system --uid 1001 latitude
77+
# Set permissions for local storage
78+
# User ID and Group ID 1001 is used to match the user 'latitude' and group 'nodejs' in the runner image.
79+
RUN set -e; \
80+
mkdir -p /app/storage/files; \
81+
mkdir -p /app/apps/web/public/files; \
82+
chown -R 1001:1001 /app/storage/files /app/apps/web/public/files
7783
USER latitude
7884

7985
WORKDIR /app

docker-compose.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
x-common-service: &common-service
22
env_file:
33
- .env
4+
volumes:
5+
- shared-storage:/app/storage/files:rw
6+
- shared-storage:/app/apps/web/public/files:rw
47
depends_on:
58
- db
69
- redis
@@ -175,7 +178,8 @@ services:
175178
build:
176179
context: .
177180
dockerfile: packages/core/docker/Dockerfile
178-
181+
labels:
182+
- "traefik.enable=false"
179183
depends_on:
180184
- db
181185
profiles:
@@ -197,7 +201,6 @@ services:
197201
build:
198202
context: .
199203
dockerfile: apps/gateway/docker/Dockerfile
200-
201204
profiles:
202205
- local
203206

@@ -214,7 +217,6 @@ services:
214217
build:
215218
context: .
216219
dockerfile: apps/workers/docker/Dockerfile
217-
218220
profiles:
219221
- local
220222

@@ -231,7 +233,6 @@ services:
231233
build:
232234
context: .
233235
dockerfile: apps/websockets/docker/Dockerfile
234-
235236
profiles:
236237
- local
237238

@@ -245,4 +246,7 @@ services:
245246
# You need to create external network for Traefik to work (docker network create web)
246247
networks:
247248
web:
248-
external: true
249+
external: true
250+
251+
volumes:
252+
shared-storage:

docs/guides/self-hosted/production.mdx

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,67 @@ Make sure this network is created before running the containers with `docker co
7171
- **Storage Configuration**:
7272

7373
- `DRIVE_DISK`: Choose between `local` or `s3` for file storage
74-
- AWS S3 credentials (if using S3 storage)
74+
1. `local` file storage configuration:
75+
- Files are stored locally on the host machine using Docker volumes.
76+
- Default variables used:
77+
78+
```bash
79+
# Paths for local storage (optional)
80+
FILE_STORAGE_ROOT_PATH="/app/storage/files" # e.g /app/storage/files
81+
FILE_PUBLIC_PATH="files" # e.g files
82+
FILES_STORAGE_PATH="/app/storage/files" # e.g /app/storage/files
83+
PUBLIC_FILES_STORAGE_PATH="/app/apps/web/public/files" # e.g /app/storage/public/files (IMPORTANT: has to be in nextjs's public folder)
84+
```
85+
86+
2. `s3` AWS S3 storage configuration:
87+
- With environment variables (for convenience/legacy):
88+
You explicitly provide AWS credentials (AWS_ACCESS_KEY and AWS_ACCESS_SECRET) via .env file.
89+
Required variables:
90+
91+
```bash
92+
AWS_ACCESS_KEY=your-access-key-here
93+
AWS_ACCESS_SECRET=your-secret-here
94+
AWS_REGION=us-east-1
95+
S3_BUCKET=app-files-bucket
96+
PUBLIC_S3_BUCKET=public-files-bucket
97+
```
98+
- AWS S3 with IAM Roles (recommended):
99+
No explicit AWS keys needed!
100+
Use IAM Roles attached to your AWS services (ECS, EC2, Lambda).
101+
Ensure AWS resource has proper IAM Role with S3 access (GetObject, PutObject, DeleteObject).
102+
Only required environment variables (no keys explicitly stored):
103+
104+
```bash
105+
AWS_REGION=us-east-1
106+
S3_BUCKET=app-files-bucket
107+
PUBLIC_S3_BUCKET=public-files-bucket
108+
```
109+
How to configure IAM Role (example):
110+
1. Go to AWS IAM → Create Role.
111+
2. Select trusted entity type (e.g., AWS Service).
112+
3. Attach policy that allows access to required S3 buckets.
113+
114+
```json
115+
{
116+
"Version": "2012-10-17",
117+
"Statement": [{
118+
"Effect": "Allow",
119+
"Action": [
120+
"s3:GetObject",
121+
"s3:PutObject",
122+
"s3:DeleteObject",
123+
"s3:ListBucket"
124+
],
125+
"Resource": [
126+
"arn:aws:s3:::your-bucket-name-here",
127+
"arn:aws:s3:::your-bucket-name-here/*"
128+
]
129+
}]
130+
}
131+
```
132+
4. Attach this IAM role to AWS infrastructure (EC2 Instance / ECS Task Definition / Lambda function).
133+
134+
AWS SDK automatically handles credentials from attached IAM roles.
75135

76136
- **Optional Features**:
77137
- Sentry integration for error tracking

packages/core/src/lib/disk.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,32 @@ const generateUrl =
2424
* AWS_ACCESS_SECRET=[your-secret]
2525
*/
2626
function getAwsConfig() {
27-
const accessKeyId = env.AWS_ACCESS_KEY
2827
const bucket = env.S3_BUCKET
2928
const publicBucket = env.PUBLIC_S3_BUCKET
3029
const region = env.AWS_REGION
31-
const secretAccessKey = env.AWS_ACCESS_SECRET
3230

33-
if (!accessKeyId || !secretAccessKey || !bucket || !publicBucket || !region) {
31+
if (!bucket || !publicBucket || !region)
3432
throw new Error(
35-
'AWS credentials not configured. Check you setup AWS_ACCESS_KEY, AWS_ACCESS_SECRET, (PUBLIC)_S3_BUCKET and AWS_REGION in your .env file.',
33+
`Missing required AWS configuration variables: ${[!bucket && 'S3_BUCKET', !publicBucket && 'PUBLIC_S3_BUCKET', !region && 'AWS_REGION'].filter(Boolean).join(', ')}.`,
3634
)
35+
36+
const accessKeyId = env.AWS_ACCESS_KEY
37+
const secretAccessKey = env.AWS_ACCESS_SECRET
38+
39+
if (accessKeyId && secretAccessKey) {
40+
return {
41+
region,
42+
bucket,
43+
publicBucket,
44+
credentials: { accessKeyId, secretAccessKey },
45+
}
3746
}
3847

48+
// If AWS_ACCESS_KEY and AWS_ACCESS_SECRET are not set, the SDK will use AWS IAM role.
3949
return {
4050
region,
4151
bucket,
4252
publicBucket,
43-
credentials: { accessKeyId, secretAccessKey },
4453
}
4554
}
4655

0 commit comments

Comments
 (0)