I'm quite new to AWS and docker.
In my local development environment, I want to upload files from the app server and place the files in localstack s3. However, when I try to access localstack s3 from the app server, the following error occurs.
AggregateError [ECONNREFUSED]:
at internalConnectMultiple (node:net:1139:18)
at afterConnectMultiple (node:net:1712:7) {
code: 'ECONNREFUSED',
'$metadata': { attempts: 3, totalRetryDelay: 105 },
[errors]: [
Error: connect ECONNREFUSED ::1:4566
at createConnectionError (node:net:1675:14)
at afterConnectMultiple (node:net:1705:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '::1',
port: 4566
},
Error: connect ECONNREFUSED 127.0.0.1:4566
at createConnectionError (node:net:1675:14)
at afterConnectMultiple (node:net:1705:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 4566
}
]
}
The docker-compose configuration is as follows. I used this link as reference.
version: '3'
services:
test-app:
build:
dockerfile: Dockerfile
context: .
args:
- VARIANT=22-bookworm
container_name: test-app
stdin_open: true
dns:
# Set the DNS server to be the LocalStack container
- 10.0.2.20
networks:
- ls
localstack:
container_name: localstack
image: localstack/localstack
ports:
- "4566:4566"
- "4510-4559:4510-4559"
environment:
- SERVICES=s3
networks:
ls:
# Set the container IP address in the 10.0.2.0/24 subnet
ipv4_address: 10.0.2.20
networks:
ls:
ipam:
config:
# Specify the subnet range for IP address allocation
- subnet: 10.0.2.0/24
The app runs on NestJS and the S3Client is initialized and injected with the following code snippet:
import { S3Client } from '@aws-sdk/client-s3';
import type { Provider } from '@nestjs/common';
export const S3_CLIENT_TOKEN = 'S3Client';
export const s3ClientProvider: Provider = {
provide: S3_CLIENT_TOKEN,
useValue: new S3Client({
endpoint: 'https://localhost.localstack.cloud:4566',
region: 'ap-northeast-1',
credentials: {
accessKeyId: '',
secretAccessKey: '',
},
}),
};
The Controller works with the following code snippet:
@Post()
async uploadFile(
@AuthorizedUser() _user: User,
@UploadedFile() file: Express.Multer.File,
): Promise<void> {
const command = new PutObjectCommand({
Bucket: 'test',
Key: file.filename,
Body: await readFile(file.path),
});
const response = await this.s3Client.send(command);
return;
}
Can anyone please help me with this? Thanks in advance :)
I expected for a basket not existing error rather than a network error, or for the file upload to be successful.
I tried the steps in this link but I didn't get the results I expected.
As everywhere else in Docker, the special IP address 127.0.0.1 refers to "the current container", and not your other container. Official AWS S3 uses a scheme where different buckets are distinguished by host name, and the main function of the external localhost.localstack.cloud
service is to allow DNS resolution to still work if you're using that system.
In Docker, to connect to localstack S3, you need to
So your configuration block should look like
export const s3ClientProvider: Provider = {
provide: S3_CLIENT_TOKEN,
useValue: new S3Client({
endpoint: 'https://localstack:4566', // the other container's Compose service name
forcePathStyle: true, // add this setting
region: 'ap-northeast-1',
credentials: {
accessKeyId: '',
secretAccessKey: '',
},
}),
};
With this, you can remove a lot of the settings in your Compose file. You don't need to manually set up container DNS; you don't need to override the Compose-generated container names; the localstack
host name works so long as both containers are on the same network, including the default
network that Compose automatically creates; and so on. You can probably reduce the Compose file to just
version: '3.8'
services:
test-app:
build:
context: .
args:
- VARIANT=22-bookworm
localstack:
image: localstack/localstack
ports: # optional, only needed if you want to access LocalStack from the host
- "4566:4566"
- "4510-4559:4510-4559"
environment:
- SERVICES=s3