R
Railway•7mo ago
bigviking

Connecting to Private Services Node API <> Redis

My node js sever is having troubling finding the redis/redis-stack-server image in the network. I'm using the traditional 6379 port and the redis-stack-server.railway.internal host name but my node DNS keeps throwing [ioredis] Unhandled error event: Error: getaddrinfo ENOTFOUND redis-stack-server.railway.internal at GetAddrInfoReqWrap.onlookup [as oncomplete] (node:dns:107:26) When I spin up my node server. This works fine on local and together in a docker compose file with the same redis image locally. What am I doing wrong to connect to the redis-stack-server?
Solution:
I went with this set-up and its all working: Dockerfile ```# Start with a node base image FROM node:21...
Jump to solution
36 Replies
Percy
Percy•7mo ago
Project ID: N/A
Brody
Brody•7mo ago
are you using ioredis?
bigviking
bigviking•7mo ago
yes
Brody
Brody•7mo ago
show me the connection object please
bigviking
bigviking•7mo ago
const options = { host: REDIS_HOST, port: REDIS_PORT, retryStrategy: (times) => { return Math.min(times * 50, 2000); }, }; const pubsub = new RedisPubSub({ publisher: new Redis(options), subscriber: new Redis(options), }); PORT: 6379, HOST: redis-stack-server.railway.internal
Brody
Brody•7mo ago
triple backticks btw
bigviking
bigviking•7mo ago
ty
Brody
Brody•7mo ago
you're missing username, password, and, family: 0
bigviking
bigviking•7mo ago
If I have the UN and PW then the address will resolve? At the moment it seems like it can't pick up the internal addy
Brody
Brody•7mo ago
if you have family: 0 the address should resolve, you'd need the username and password regardless
bigviking
bigviking•7mo ago
interesting, may I ask why 'family' is required? So ...
const options = {
host: REDIS_HOST,
port: REDIS_PORT,
username: REDIS_USER,
password: REDIS_PASSWORD,
family: 0,
retryStrategy: (times) => {
// reconnect after
return Math.min(times * 50, 2000);
},
};
const options = {
host: REDIS_HOST,
port: REDIS_PORT,
username: REDIS_USER,
password: REDIS_PASSWORD,
family: 0,
retryStrategy: (times) => {
// reconnect after
return Math.min(times * 50, 2000);
},
};
?
Brody
Brody•7mo ago
the internal network is ipv6 only, ioredis assumes ipv4 by default, family: 0 tells ioredis to use both ipv6 and ipv4 yeah looks good
bigviking
bigviking•7mo ago
I knew it had something to do with the IPv6 thanks I'll get back to hacking 🚀
Brody
Brody•7mo ago
no problem, let me know if it works though this is how i would do it in the variables for your app, reference the redis database's private url like this
REDIS_PRIVATE_URL=${{Redis.REDIS_PRIVATE_URL}}
REDIS_PRIVATE_URL=${{Redis.REDIS_PRIVATE_URL}}
then connect to redis like this
import Redis from "ioredis";

const redisURL = new URL(process.env.REDIS_PRIVATE_URL);

const redis = new Redis({
family: 0,
host: redisURL.hostname,
port: redisURL.port,
username: redisURL.username,
password: redisURL.password
});
import Redis from "ioredis";

const redisURL = new URL(process.env.REDIS_PRIVATE_URL);

const redis = new Redis({
family: 0,
host: redisURL.hostname,
port: redisURL.port,
username: redisURL.username,
password: redisURL.password
});
bigviking
bigviking•7mo ago
Hmm.. here's the stack trace. Still throwing the ENOTFOUND with the private address
No description
Brody
Brody•7mo ago
you may need to add a 3 second sleep to your start script whats your current start script
bigviking
bigviking•7mo ago
const main = async () => {
// Apollo/GraphQL server start.
await server.start();

if (server) {
logger.info(
`Apollo/GraphQL API is now live on endpoint: ${NODE_PORT}/graphql`
);
} else {
logger.fatal(`Could not start Apollo/GraphQL API`);
process.exit(1);
}

// Middleware for the express application.
app.use(
'/graphql',
cors(),
helmet(),
limiter,
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
return { prisma, pubsub, req };
},
})
);

app.get('/health', (req, res) => {
res.status(200).send('Okay!');
});

// WS server start
httpServer.listen(NODE_PORT, () => {
logger.info(
`Apollo/GraphQL websocket service is live on endpoint: ${NODE_PORT}/graphql`
);
});
};

main();
const main = async () => {
// Apollo/GraphQL server start.
await server.start();

if (server) {
logger.info(
`Apollo/GraphQL API is now live on endpoint: ${NODE_PORT}/graphql`
);
} else {
logger.fatal(`Could not start Apollo/GraphQL API`);
process.exit(1);
}

// Middleware for the express application.
app.use(
'/graphql',
cors(),
helmet(),
limiter,
bodyParser.json(),
expressMiddleware(server, {
context: async ({ req }) => {
return { prisma, pubsub, req };
},
})
);

app.get('/health', (req, res) => {
res.status(200).send('Okay!');
});

// WS server start
httpServer.listen(NODE_PORT, () => {
logger.info(
`Apollo/GraphQL websocket service is live on endpoint: ${NODE_PORT}/graphql`
);
});
};

main();
is that what you're looking for?
Brody
Brody•7mo ago
nope, im looking for your start script, it would be in your package.json
bigviking
bigviking•7mo ago
oh yeah duh one second "prod": "NODE_ENV=production node app.js",
Brody
Brody•7mo ago
how have you told railway to run that script?
bigviking
bigviking•7mo ago
It runs from a Dockerfile I have in the project
# Start your image with a node base image
FROM node:21

# The /app directory should act as the main application directory
WORKDIR /app

# Copy the app package and package-lock.json file
COPY package*.json ./
COPY package-lock.json package-lock.json

# Copy local directories to the current local directory of our docker image (/app)
COPY . .

# Install node packages.
RUN npm install

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "prod"]
# Start your image with a node base image
FROM node:21

# The /app directory should act as the main application directory
WORKDIR /app

# Copy the app package and package-lock.json file
COPY package*.json ./
COPY package-lock.json package-lock.json

# Copy local directories to the current local directory of our docker image (/app)
COPY . .

# Install node packages.
RUN npm install

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "prod"]
Brody
Brody•7mo ago
do you use this Dockerfile locally too?
bigviking
bigviking•7mo ago
yes
Brody
Brody•7mo ago
okay then in your service settings, set the start command to sleep 3 && npm run prod and just a quick question, why Dockerfile instead of nixpacks?
bigviking
bigviking•7mo ago
I didn't really know what nixpacks was until I started working on Railway last week
Brody
Brody•7mo ago
fair enough mind if i make a suggestion on your dockerfile?
bigviking
bigviking•7mo ago
Sure thing. I'm relatively new to coding ~2 years.
Brody
Brody•7mo ago
nothing major
# Start your image with a node base image
FROM node:21

ENV NPM_CONFIG_UPDATE_NOTIFIER false
ENV NPM_CONFIG_FUND false

# The /app directory should act as the main application directory
WORKDIR /app

# Copy in the package and package-lock.json file
COPY package*.json ./

# Install node packages
RUN npm ci

# Copy local directories to the current local directory of our docker image (/app)
COPY . ./

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "prod"]
# Start your image with a node base image
FROM node:21

ENV NPM_CONFIG_UPDATE_NOTIFIER false
ENV NPM_CONFIG_FUND false

# The /app directory should act as the main application directory
WORKDIR /app

# Copy in the package and package-lock.json file
COPY package*.json ./

# Install node packages
RUN npm ci

# Copy local directories to the current local directory of our docker image (/app)
COPY . ./

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "prod"]
bigviking
bigviking•7mo ago
neat, I'll have a look at the ci command thanks very much. One question, when you mention my project's service settings what are you referring to
Brody
Brody•7mo ago
im referring to the service's settings 😅
No description
bigviking
bigviking•7mo ago
I see, my fault. I thought you were referring to something related to the node app
Brody
Brody•7mo ago
no worries
bigviking
bigviking•7mo ago
Wanted to follow up and thank you for all the help. The "sleep 3 &&.." custom start command completes the build just fine but I see no deployment logs. The service reports completed but that doesn't seem normal.
Brody
Brody•7mo ago
completed means it exited without an error code try setting the start command to
/bin/sh -c "sleep 3 && npm run start"
/bin/sh -c "sleep 3 && npm run start"
Solution
bigviking
bigviking•7mo ago
I went with this set-up and its all working: Dockerfile
# Start with a node base image
FROM node:21

ENV NPM_CONFIG_UPDATE_NOTIFIER false
ENV NPM_CONFIG_FUND false

# The /app directory should act as the main application directory
WORKDIR /app

# Copy in the package and package-lock.json file
COPY package*.json ./

# Install node packages
RUN npm ci

# Copy local directories to the current local directory of our docker image (/app)
COPY . .

RUN npx prisma generate

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "railway"]
# Start with a node base image
FROM node:21

ENV NPM_CONFIG_UPDATE_NOTIFIER false
ENV NPM_CONFIG_FUND false

# The /app directory should act as the main application directory
WORKDIR /app

# Copy in the package and package-lock.json file
COPY package*.json ./

# Install node packages
RUN npm ci

# Copy local directories to the current local directory of our docker image (/app)
COPY . .

RUN npx prisma generate

# Expose the server port
EXPOSE 8080

# Start the container
CMD [ "npm", "run", "railway"]
Start scripts
"railway": "sleep 3 && npm run prod",
"prod": "export NODE_ENV=production && node app.js",
"railway": "sleep 3 && npm run prod",
"prod": "export NODE_ENV=production && node app.js",
Brody
Brody•7mo ago
thats a very good way of doing it