@spree/docs 0.1.129 → 0.1.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,466 +1,109 @@
1
1
  ---
2
- title: Amazon Web Services (AWS)
3
- description: Learn how to deploy your Spree application on Amazon Web Services (AWS).
2
+ title: AWS EC2 + RDS
3
+ sidebarTitle: EC2 + RDS
4
+ description: The simplest production Spree on AWS — one EC2 instance and a managed database.
4
5
  ---
5
6
 
6
- Amazon Web Services offers reliable, scalable, and inexpensive cloud computing services. AWS is also one of the most popular choices for hosting a Spree application.
7
+ The simplest way to run Spree on AWS is the [single-node topology](quickstart.md#single-node-vs-distributed): **one EC2 instance running the Docker image, one RDS database**. No clusters, no load balancers, no task definitions — a complete production store for roughly the cost of a t3.small and a small RDS instance.
7
8
 
8
- We recommend using AWS ECS Fargate to host your Spree application via [Docker image](docker.md).
9
+ Need auto-scaling, zero-downtime deploys, or CI/CD-driven infrastructure? That's the [ECS Fargate guide](aws_ecs.md) it runs the same Docker image, so you can start here and graduate later without rework.
9
10
 
10
- ## Required AWS Services
11
+ ## What You'll Create
11
12
 
12
- To fully run your Spree application on AWS, you will need the following services:
13
-
14
- | Service | Description |
15
- |---------|-------------|
16
- | [AWS ECS Fargate](https://aws.amazon.com/fargate/) | Fully managed container orchestration — run and scale containers without managing infrastructure. |
17
- | [AWS RDS](https://aws.amazon.com/rds/) | Managed relational database. Spree works with [Aurora PostgreSQL](https://aws.amazon.com/rds/aurora/), [Aurora MySQL](https://aws.amazon.com/rds/aurora/), [RDS PostgreSQL](https://aws.amazon.com/rds/postgresql/), [RDS MySQL](https://aws.amazon.com/rds/mysql/), and [RDS MariaDB](https://aws.amazon.com/rds/mariadb/). |
18
- | [AWS ElastiCache](https://aws.amazon.com/elasticache/) | Valkey or Redis for background jobs (Sidekiq), [caching](caching.md), and Action Cable. We recommend separate instances for jobs and cache. |
19
- | [AWS S3](https://aws.amazon.com/s3/) | Object storage for uploaded files (product images, etc.). [More information](assets.md#aws-s3). |
20
- | [AWS CloudFront](https://aws.amazon.com/cloudfront/) | CDN for asset delivery (images, stylesheets, JavaScript). |
21
- | [AWS Route 53](https://aws.amazon.com/route53/) | DNS service for domain name management. |
22
- | [AWS Certificate Manager](https://aws.amazon.com/certificate-manager/) | Free SSL/TLS certificates. Spree requires HTTPS in production. |
23
- | [AWS ECR](https://aws.amazon.com/ecr/) | Docker container registry for storing your application images. |
24
-
25
- ## Docker Image
26
-
27
- You can use the [official Docker image](docker.md) (`ghcr.io/spree/spree`) directly, or build your own from your Rails application's Dockerfile.
28
-
29
- To build and deploy a custom image to AWS ECR via GitHub Actions:
30
-
31
- ```yaml
32
- name: Deploy to AWS Fargate
33
-
34
- on:
35
- push:
36
- branches: [ main ]
37
- pull_request:
38
- branches: [ main ]
39
-
40
- env:
41
- AWS_REGION: us-east-1
42
- ECR_REPOSITORY: spree-starter
43
- ECS_SERVICE_WEB: spree-web
44
- ECS_SERVICE_WORKER: spree-worker
45
- ECS_CLUSTER: spree-cluster
46
-
47
- jobs:
48
- build:
49
- name: Build and Push to ECR
50
- runs-on: ubuntu-latest
51
-
52
- outputs:
53
- image: ${{ steps.build-image.outputs.image }}
54
- image-tag: ${{ steps.build-image.outputs.image-tag }}
55
-
56
- steps:
57
- - name: Checkout code
58
- uses: actions/checkout@v4
59
-
60
- - name: Configure AWS credentials
61
- uses: aws-actions/configure-aws-credentials@v4
62
- with:
63
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
64
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
65
- aws-region: ${{ env.AWS_REGION }}
66
-
67
- - name: Login to Amazon ECR
68
- id: login-ecr
69
- uses: aws-actions/amazon-ecr-login@v2
70
-
71
- - name: Build, tag, and push image to Amazon ECR
72
- id: build-image
73
- env:
74
- ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
75
- IMAGE_TAG: ${{ github.sha }}
76
- run: |
77
- # Build a docker container and push it to ECR
78
- docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
79
- docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
80
- echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
81
- echo "image-tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
82
-
83
- deploy-web:
84
- name: Deploy Web Service
85
- runs-on: ubuntu-latest
86
- needs: build
87
- if: github.ref == 'refs/heads/main'
88
-
89
- steps:
90
- - name: Checkout code
91
- uses: actions/checkout@v4
92
-
93
- - name: Configure AWS credentials
94
- uses: aws-actions/configure-aws-credentials@v4
95
- with:
96
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
97
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
98
- aws-region: ${{ env.AWS_REGION }}
99
-
100
- - name: Fill in the new image ID in the Amazon ECS task definition
101
- id: task-def-web
102
- uses: aws-actions/amazon-ecs-render-task-definition@v1
103
- env:
104
- ECR_REGISTRY: ${{ needs.build.outputs.image }}
105
- IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
106
- AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
107
- with:
108
- task-definition: .aws/web-task-definition.json
109
- container-name: web
110
- image: ${{ needs.build.outputs.image }}
111
-
112
- - name: Deploy Amazon ECS task definition for web
113
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
114
- with:
115
- task-definition: ${{ steps.task-def-web.outputs.task-definition }}
116
- service: ${{ env.ECS_SERVICE_WEB }}
117
- cluster: ${{ env.ECS_CLUSTER }}
118
- wait-for-service-stability: true
119
-
120
- deploy-worker:
121
- name: Deploy Worker Service
122
- runs-on: ubuntu-latest
123
- needs: build
124
- if: github.ref == 'refs/heads/main'
125
-
126
- steps:
127
- - name: Checkout code
128
- uses: actions/checkout@v4
129
-
130
- - name: Configure AWS credentials
131
- uses: aws-actions/configure-aws-credentials@v4
132
- with:
133
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
134
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
135
- aws-region: ${{ env.AWS_REGION }}
136
-
137
- - name: Fill in the new image ID in the Amazon ECS task definition
138
- id: task-def-worker
139
- uses: aws-actions/amazon-ecs-render-task-definition@v1
140
- env:
141
- ECR_REGISTRY: ${{ needs.build.outputs.image }}
142
- IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
143
- AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
144
- with:
145
- task-definition: .aws/worker-task-definition.json
146
- container-name: worker
147
- image: ${{ needs.build.outputs.image }}
148
-
149
- - name: Deploy Amazon ECS task definition for worker
150
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
151
- with:
152
- task-definition: ${{ steps.task-def-worker.outputs.task-definition }}
153
- service: ${{ env.ECS_SERVICE_WORKER }}
154
- cluster: ${{ env.ECS_CLUSTER }}
155
- wait-for-service-stability: true
156
-
157
- migrate:
158
- name: Run Database Migrations
159
- runs-on: ubuntu-latest
160
- needs: [build, deploy-web]
161
- if: github.ref == 'refs/heads/main'
162
-
163
- steps:
164
- - name: Checkout code
165
- uses: actions/checkout@v4
166
-
167
- - name: Configure AWS credentials
168
- uses: aws-actions/configure-aws-credentials@v4
169
- with:
170
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
171
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
172
- aws-region: ${{ env.AWS_REGION }}
173
-
174
- - name: Run database migrations
175
- run: |
176
- aws ecs run-task \
177
- --cluster ${{ env.ECS_CLUSTER }} \
178
- --task-definition spree-web \
179
- --overrides '{
180
- "containerOverrides": [{
181
- "name": "web",
182
- "command": ["bundle", "exec", "rails", "db:migrate"]
183
- }]
184
- }' \
185
- --launch-type FARGATE \
186
- --network-configuration '{
187
- "awsvpcConfiguration": {
188
- "subnets": ["'${{ secrets.SUBNET_ID_1 }}'", "'${{ secrets.SUBNET_ID_2 }}'"],
189
- "securityGroups": ["'${{ secrets.SECURITY_GROUP_ID }}'"],
190
- "assignPublicIp": "ENABLED"
191
- }
192
- }'
193
- ```
194
-
195
- This action requires secrets to be set in your GitHub repository. You can find the full list of secrets in the [AWS ECS Deploy Task Definition](https://github.com/aws-actions/amazon-ecs-deploy-task-definition) GitHub Actions repository.
196
-
197
- | Secret | Description |
198
- |--------|-------------|
199
- | `AWS_ACCESS_KEY_ID` | AWS access key ID |
200
- | `AWS_SECRET_ACCESS_KEY` | AWS secret access key |
201
- | `AWS_ACCOUNT_ID` | AWS account ID |
202
- | `SUBNET_ID_1` | First subnet ID |
203
- | `SUBNET_ID_2` | Second subnet ID |
204
- | `SECURITY_GROUP_ID` | Security group ID |
205
-
206
- ## Environment Variables
207
-
208
- Store secrets in [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/) and reference them in your task definitions. Non-sensitive configuration goes in the `environment` array directly.
209
-
210
- For a full list of available variables, see [Environment Variables](environment_variables.md).
211
-
212
- ### Secrets Manager
213
-
214
- Create the following secrets in AWS Secrets Manager:
215
-
216
- | Secret Name | Variable | Description |
13
+ | Resource | Purpose | Starting size |
217
14
  |---|---|---|
218
- | `spree/database-url` | `DATABASE_URL` | PostgreSQL connection URL |
219
- | `spree/redis-url` | `REDIS_URL` | Redis URL for background jobs and Action Cable |
220
- | `spree/redis-cache-url` | `REDIS_CACHE_URL` | Redis URL for caching (separate instance recommended) |
221
- | `spree/secret-key-base` | `SECRET_KEY_BASE` | Generate with `bin/rails secret` |
222
-
223
- Optional secrets for email delivery, file storage, and error tracking:
15
+ | **EC2 instance** | Runs the Spree container (web + background jobs) | `t3.small` (2 GB) to start, `t3.medium` for comfort |
16
+ | **RDS PostgreSQL** | The only backing service data, jobs, cache | `db.t4g.small` |
17
+ | **DNS record** | Your store's web address, e.g. `store.example.com`, pointing at the instance's Elastic IP (a free static IP — a plain EC2 IP changes on restart and would break the record) | — |
18
+ | **S3 bucket** (recommended) | Uploaded files (product images) — they shouldn't live on the instance disk. See [Asset Storage](assets.md) | — |
224
19
 
225
- | Secret Name | Variable | Description |
226
- |---|---|---|
227
- | `spree/smtp-password` | `SMTP_PASSWORD` | SMTP auth password |
228
- | `spree/sentry-dsn` | `SENTRY_DSN` | Sentry DSN for error tracking |
20
+ ## 1. Create the Database (RDS)
229
21
 
230
- > **TIP:** S3 file storage credentials are not needed as environment variables when your ECS task role has the appropriate S3 permissions. Use IAM roles instead of access keys when possible.
22
+ Create an **RDS PostgreSQL** instance in the AWS console:
231
23
 
232
- ## ECS Task Definitions
24
+ - Same VPC as your EC2 instance will use
25
+ - **Public access: no** — instead, allow inbound port `5432` from the EC2 instance's security group
26
+ - Note the endpoint, username, and password — they become your `DATABASE_URL`
233
27
 
234
- You will need two ECS task definitions: one for the web service and one for the worker service. Save these as `.aws/web-task-definition.json` and `.aws/worker-task-definition.json` in your repository.
28
+ Spree also runs on RDS MySQL/MariaDB and both Aurora flavors see [Database Configuration](database.md). RDS takes automated daily backups by default, so the database needs no extra care.
235
29
 
236
- ### Web Service
30
+ ## 2. Launch the Instance
237
31
 
238
- ```json
239
- {
240
- "family": "spree-web",
241
- "networkMode": "awsvpc",
242
- "requiresCompatibilities": ["FARGATE"],
243
- "cpu": "1024",
244
- "memory": "4096",
245
- "executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
246
- "taskRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskRole",
247
- "containerDefinitions": [
248
- {
249
- "name": "web",
250
- "image": "${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}",
251
- "portMappings": [
252
- {
253
- "containerPort": 3000,
254
- "protocol": "tcp"
255
- }
256
- ],
257
- "essential": true,
258
- "environment": [
259
- {
260
- "name": "RAILS_ENV",
261
- "value": "production"
262
- },
263
- {
264
- "name": "PORT",
265
- "value": "3000"
266
- },
267
- {
268
- "name": "RAILS_MAX_THREADS",
269
- "value": "3"
270
- },
271
- {
272
- "name": "WEB_CONCURRENCY",
273
- "value": "auto"
274
- },
275
- {
276
- "name": "RAILS_LOG_LEVEL",
277
- "value": "info"
278
- },
279
- {
280
- "name": "AWS_BUCKET",
281
- "value": "your-spree-bucket"
282
- }
283
- ],
284
- "secrets": [
285
- {
286
- "name": "DATABASE_URL",
287
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/database-url"
288
- },
289
- {
290
- "name": "REDIS_URL",
291
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-url"
292
- },
293
- {
294
- "name": "REDIS_CACHE_URL",
295
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-cache-url"
296
- },
297
- {
298
- "name": "SECRET_KEY_BASE",
299
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
300
- }
301
- ],
302
- "logConfiguration": {
303
- "logDriver": "awslogs",
304
- "options": {
305
- "awslogs-group": "/ecs/spree-web",
306
- "awslogs-region": "${AWS_REGION}",
307
- "awslogs-stream-prefix": "ecs"
308
- }
309
- },
310
- "healthCheck": {
311
- "command": ["CMD-SHELL", "curl -f http://localhost:3000/up || exit 1"],
312
- "interval": 30,
313
- "timeout": 5,
314
- "retries": 3,
315
- "startPeriod": 60
316
- }
317
- }
318
- ]
319
- }
320
- ```
32
+ Launch an EC2 instance (Ubuntu 24.04 or Amazon Linux 2023) and open ports `80` and `443` in its security group. Give it a stable web address: associate an Elastic IP (so the instance's IP survives restarts) and create a DNS record for your domain — e.g. an `A` record for `store.example.com` — pointing at it. HTTPS depends on this: Caddy can only obtain a certificate once the domain resolves to the instance.
321
33
 
322
- ### Worker Service
34
+ Then install Docker:
323
35
 
324
- ```json
325
- {
326
- "family": "spree-worker",
327
- "networkMode": "awsvpc",
328
- "requiresCompatibilities": ["FARGATE"],
329
- "cpu": "512",
330
- "memory": "2048",
331
- "executionRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskExecutionRole",
332
- "taskRoleArn": "arn:aws:iam::${AWS_ACCOUNT_ID}:role/ecsTaskRole",
333
- "containerDefinitions": [
334
- {
335
- "name": "worker",
336
- "image": "${ECR_REGISTRY}/${ECR_REPOSITORY}:${IMAGE_TAG}",
337
- "command": ["bundle", "exec", "sidekiq"],
338
- "essential": true,
339
- "environment": [
340
- {
341
- "name": "RAILS_ENV",
342
- "value": "production"
343
- },
344
- {
345
- "name": "RAILS_LOG_LEVEL",
346
- "value": "info"
347
- }
348
- ],
349
- "secrets": [
350
- {
351
- "name": "DATABASE_URL",
352
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/database-url"
353
- },
354
- {
355
- "name": "REDIS_URL",
356
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/redis-url"
357
- },
358
- {
359
- "name": "SECRET_KEY_BASE",
360
- "valueFrom": "arn:aws:secretsmanager:${AWS_REGION}:${AWS_ACCOUNT_ID}:secret:spree/secret-key-base"
361
- }
362
- ],
363
- "logConfiguration": {
364
- "logDriver": "awslogs",
365
- "options": {
366
- "awslogs-group": "/ecs/spree-worker",
367
- "awslogs-region": "${AWS_REGION}",
368
- "awslogs-stream-prefix": "ecs"
369
- }
370
- },
371
- "healthCheck": {
372
- "command": ["CMD-SHELL", "pgrep -f sidekiq || exit 1"],
373
- "interval": 30,
374
- "timeout": 5,
375
- "retries": 3,
376
- "startPeriod": 60
377
- }
378
- }
379
- ]
380
- }
36
+ ```bash
37
+ curl -fsSL https://get.docker.com | sh
381
38
  ```
382
39
 
383
- ## Production Sizing
384
-
385
- The task definitions above are sized for production. Here is a summary and scaling guidance:
386
-
387
- | Service | Fargate CPU | Fargate Memory | Instances |
388
- |---|---|---|---|
389
- | **Web** | 1 vCPU | 4 GB | 2+ (use ECS Service Auto Scaling) |
390
- | **Worker** | 0.5 vCPU | 2 GB | 1+ |
391
- | **RDS (PostgreSQL)** | — | `db.r6g.large` (2 vCPU, 16 GB) | 1 primary + read replica |
392
- | **ElastiCache (jobs)** | — | `cache.r6g.large` (13 GB) | 1 |
393
- | **ElastiCache (cache)** | — | `cache.r6g.large` (13 GB) | 1 |
394
-
395
- ### Auto Scaling
396
-
397
- Use [ECS Service Auto Scaling](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html) to scale the web service based on CPU or memory utilization:
40
+ ## 3. Run Spree
41
+
42
+ Build [your project's image](docker.md) and push it to a registry the instance can pull from (ECR or any other) — or use the stock `ghcr.io/spree/spree:latest` image to start. Then create a `docker-compose.yml` on the instance:
43
+
44
+ ```yaml docker-compose.yml
45
+ services:
46
+ web:
47
+ image: your-registry/my-store:latest # or ghcr.io/spree/spree:latest
48
+ restart: unless-stopped
49
+ environment:
50
+ DATABASE_URL: postgres://spree:YOUR_PASSWORD@your-db.abc123.eu-west-1.rds.amazonaws.com:5432/spree_production
51
+ SECRET_KEY_BASE: generate-with-openssl-rand-hex-64
52
+ RAILS_HOST: store.example.com
53
+ # Uploads to S3 (recommended) — see /developer/deployment/assets
54
+ # AWS_REGION: eu-west-1
55
+ # AWS_BUCKET: my-store-uploads
56
+ # AWS_ACCESS_KEY_ID: ...
57
+ # AWS_SECRET_ACCESS_KEY: ...
58
+ # Email delivery — see /developer/deployment/emails
59
+ # SMTP_HOST: smtp.resend.com
60
+ # SMTP_USERNAME: resend
61
+ # SMTP_PASSWORD: ...
62
+ # SMTP_FROM_ADDRESS: orders@example.com
63
+
64
+ # Caddy terminates HTTPS with an automatic Let's Encrypt certificate
65
+ caddy:
66
+ image: caddy:2
67
+ restart: unless-stopped
68
+ ports:
69
+ - "80:80"
70
+ - "443:443"
71
+ command: caddy reverse-proxy --from store.example.com --to web:3000
72
+ volumes:
73
+ - caddy_data:/data
74
+
75
+ volumes:
76
+ caddy_data:
77
+ ```
398
78
 
399
79
  ```bash
400
- # Register a scalable target (min 2, max 6 tasks)
401
- aws application-autoscaling register-scalable-target \
402
- --service-namespace ecs \
403
- --resource-id service/spree-cluster/spree-web \
404
- --scalable-dimension ecs:service:DesiredCount \
405
- --min-capacity 2 \
406
- --max-capacity 6
407
-
408
- # Scale based on average CPU utilization (target 70%)
409
- aws application-autoscaling put-scaling-policy \
410
- --service-namespace ecs \
411
- --resource-id service/spree-cluster/spree-web \
412
- --scalable-dimension ecs:service:DesiredCount \
413
- --policy-name spree-web-cpu-scaling \
414
- --policy-type TargetTrackingScaling \
415
- --target-tracking-scaling-policy-configuration '{
416
- "TargetValue": 70.0,
417
- "PredefinedMetricSpecification": {
418
- "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
419
- },
420
- "ScaleInCooldown": 300,
421
- "ScaleOutCooldown": 60
422
- }'
80
+ docker compose up -d
423
81
  ```
424
82
 
425
- ## After Deployment
83
+ The database is migrated automatically on boot. Once DNS resolves, your store is live at `https://store.example.com` — admin at `/admin`, background jobs running inside the web container ([combined mode](quickstart.md#web-and-worker)).
426
84
 
427
- ### Admin Dashboard
85
+ ## Updating
428
86
 
429
- Access your admin panel at:
87
+ Deploying a new version is a pull and a restart:
430
88
 
431
- ```
432
- https://<your-domain>/admin
89
+ ```bash
90
+ docker compose pull && docker compose up -d
433
91
  ```
434
92
 
435
- Default credentials are created during `db:seed`. Change them immediately after first login.
93
+ This restarts the container with a few seconds of downtime the point at which that matters is a good signal to consider [ECS Fargate](aws_ecs.md) and its rolling deploys.
436
94
 
437
- ### Database Migrations
95
+ ## Scaling on One Box
438
96
 
439
- The GitHub Actions workflow above runs migrations automatically on deploy. To run migrations manually:
97
+ Before reaching for a cluster:
440
98
 
441
- ```bash
442
- aws ecs run-task \
443
- --cluster spree-cluster \
444
- --task-definition spree-web \
445
- --overrides '{
446
- "containerOverrides": [{
447
- "name": "web",
448
- "command": ["bundle", "exec", "rails", "db:migrate"]
449
- }]
450
- }' \
451
- --launch-type FARGATE \
452
- --network-configuration '{
453
- "awsvpcConfiguration": {
454
- "subnets": ["subnet-xxx", "subnet-yyy"],
455
- "securityGroups": ["sg-xxx"],
456
- "assignPublicIp": "DISABLED"
457
- }
458
- }'
459
- ```
99
+ - **Bigger instance** — a `t3.medium` or `t3.large` carries substantial traffic; resizing is a stop/start
100
+ - **Split the worker** — add a second service to the compose file with `command: bin/jobs`, and set `SOLID_QUEUE_IN_PUMA: "false"` on `web` ([split mode](quickstart.md#web-and-worker))
101
+ - **Scale the database** — RDS resizes independently, and read replicas are a console click
460
102
 
461
103
  ## Next Steps
462
104
 
463
- - [Asset Storage](assets.md) — configure S3 for product images and uploads
464
- - [Configure CDN](cdn.md) — set up CloudFront for asset delivery
465
- - [Configure caching](caching.md) — use ElastiCache for application caching
466
- - [Set environment variables](environment_variables.md) — SMTP, Sentry, SSL, etc.
105
+ - [Asset Storage](assets.md) — store uploads in S3
106
+ - [Set environment variables](environment_variables.md) — SMTP, Sentry, etc.
107
+ - [Configure CDN](cdn.md) — CloudFront in front of your assets
108
+ - [Deploy the storefront](../storefront/nextjs/deployment.md) — the Next.js storefront ships separately, typically to Vercel
109
+ - [ECS Fargate](aws_ecs.md) — when you outgrow one box