Unable to reach internal urls
tsg-brent
PROOP

2 months ago

I'm having an issue where my frontend web service does not seem able to resolve the internal URL for the api service. My code was using api.railway.internal, but that did not resolve. When I switched the public url, the code worked as expected.

Some things I've tried are shelling into the service and trying to resolve the internal urls there. In those cases, I get a ENOTFOUND api.railway.internal error. Working with the AI agent, I tried several approaches, but none resolved the issue and finally it suggested I reach out to support.

Unfortunately, my code is in a private GitHub repo, so I can't share it. However, the web service is a NextJS app that is trying to do a fetch against the api service. The public facing URL that web service is calling is:

https://api-production-6559.up.railway.app/api/v1/cms/health

Which does work, but the equivalent api.railway.internal does not.

Solved$10 Bounty

Pinned Solution

tsg-brent
PROOP

2 months ago

I have resolved the issue. I believe in addition to the above note about enabling private networking (which, again, I swear wasn't there before), I hadn't disabled caching for the request I was making. Next was trying to resolve it at build time. I disabled that and it is now working.

11 Replies

Railway
BOT

2 months ago

This thread has been marked as public for community involvement, as it does not contain any sensitive or personal information. Any further activity in this thread will be visible to everyone.

Status changed to Open Railway 2 months ago


Anonymous
FREE

2 months ago

api.railway.internal will only resolve if the services are communicating through Railway’s private networking DNS, and that hostname is not created automatically for every service.

In Railway the internal hostname is usually based on the service name, not the public subdomain. The correct internal URL typically looks like:

http://<service-name>.railway.internal

or the full internal domain shown in the service settings.

You can find the correct internal address by going to:

Service → Settings → Private Networking

Railway usually shows something like:

your-service-name.railway.internal

or

your-service-name.railway.internal:PORT

That is the hostname other services inside the same project should use.

Why api.railway.internal may not resolve

The name only works if:

  • The actual service name is api
  • Both services are in the same Railway project/environment
  • Private networking is enabled
  • The request is made server-side, not from the browser

Since your frontend is a Next.js app, another common issue is that the request runs on the client side. Browser code cannot access *.railway.internal because it only exists inside the Railway network.

So make sure the fetch call runs:

  • in API routes
  • in server actions
  • or in getServerSideProps / server components

Example:

http://api-service-name.railway.internal:3000/api/v1/cms/health

not from browser client code.

Quick test

From the frontend service shell, try:

curl http://<api-service-name>.railway.internal:<PORT>

If that resolves, the internal DNS is working and the issue is likely that the request is happening on the client side instead of the server.

So in short:

  • *.railway.internal only works inside Railway services
  • the hostname must match the actual service name
  • requests must run server-side, not in the browser

Using the public URL works because it goes through Railway’s edge routing instead of the internal service network.


tsg-brent
PROOP

2 months ago

In response to your points:

  • The NextJS app is making the request server-side. It is part of server-side rendering.
    • A simple way I'm verifying that is by bringing up the dev tools and looking at the network panel. No request is made to the internal URL. (Or public when it is in use.)
    • In addition, I see the error result rendered in the HTML returned with the request.
  • According to the dashboard, the internal url is api.railway.internal.
  • The services are all running in the same AWS region, if that affects anything.

Another way I'm trying to testing resolving the domain is by shelling into the web service and running Node code like this:

```

$ railway shell --service web

Entering subshell with Railway variables available. Type 'exit' to exit.

$ node

Welcome to Node.js v24.14.0.

Type ".help" for more information.

> const dns = require('dns').promises;

undefined

>

> (async () => {

| try {

| const result = await dns.resolve4('api.railway.internal');

| console.log('DNS resolved to:', result);

| } catch (err) {

| console.error('DNS error:', err.message);

| }

| })();

Promise {

,

Symbol(async_id_symbol): 112,

Symbol(trigger_async_id_symbol): 6

}

> DNS error: queryA ENOTFOUND api.railway.internal

```

I would think that in this case, I'm within the server context and should be able to resolve the DNS. I've done the same with a curl from the shell session:

$ curl http://api.railway.internal
curl: (6) Could not resolve host: api.railway.internal

Are there other / better ways to try to verify if the issue is connectivity between the services or in my code?


mattijsdp
PRO

2 months ago

You should add the port when using private networking. I just tested this on my (equally named api) service:

root@f8f11e209ca3:/app# curl http://api.railway.internal

curl: (7) Failed to connect to api.railway.internal port 80 after 1084 ms: Couldn't connect to server

root@f8f11e209ca3:/app# curl http://api.railway.internal:8080

{"detail":"Not Found"}

*Not the "Not found" is just because I don't have an endpoint at / but it is connecting.


mattijsdp
PRO

2 months ago

Sorry just noticed you actually have a different error (not regarding port), ignore me. I use railway ssh if that changes anything.


tsg-brent
PROOP

2 months ago

Maybe in response to this thread, but I suddenly noticed a new button in the UI. I swear that wasn't there before...

I enabled that and also added a connectivity check as a predeployment step and that is resolving the internal url. However, my NextJS code is still not resolving, so I'll move my focus there. The railway shell also doesn't resolve the URL, however I'm getting the impression that isn't running the terminal session in the actual deployed environment, but just locally.


tsg-brent
PROOP

2 months ago

I have resolved the issue. I believe in addition to the above note about enabling private networking (which, again, I swear wasn't there before), I hadn't disabled caching for the request I was making. Next was trying to resolve it at build time. I disabled that and it is now working.


Status changed to Solved chandrika 2 months ago


chandrika
EMPLOYEE

2 months ago

Hey there, glad you got this working 🙌 You're right that railway shell runs locally with Railway environment variables injected, not inside the deployed container, so private DNS won't resolve there (you'd want to use railway ssh if you want to shell into the actual running container


Status changed to Awaiting User Response Railway 2 months ago


Status changed to Solved chandrika 2 months ago


chandrika

Hey there, glad you got this working 🙌 You're right that `railway shell` runs locally with Railway environment variables injected, not inside the deployed container, so private DNS won't resolve there (you'd want to use `railway ssh` if you want to shell into the actual running container

tsg-brent
PROOP

2 months ago

Now that my production environment is up and running, I created a staging environment and I'm facing the same issue. Did I miss some step that enables private networking? There's no button to enable it here.

Attachments


Status changed to Awaiting Railway Response Railway 2 months ago


chandrika
EMPLOYEE

2 months ago

Hi there, private networking is enabled by default for all environments, which is why there's no button. Your staging screenshot shows api.railway.internal as "Ready to talk privately" with IPv4 & IPv6, so the private network is already active.

You likely need to apply the same Next.js fetch caching fix from production - ensuring your fetch calls use { cache: 'no-store' } or equivalent so they resolve at runtime rather than build time.


Status changed to Awaiting User Response Railway 2 months ago


tsg-brent
PROOP

2 months ago

I believe the issue was that API hadn't fully deployed yet and my connectivity check failed. I'm still very curious about that button I had to click earlier on my production environment. Anyway, all is well.


Status changed to Awaiting Railway Response Railway 2 months ago


chandrika
EMPLOYEE

2 months ago

Glad it's sorted! If your production environment was old, the "Enable Private Networking" button was there likely due to that — this is now enabled by default so the buttons doesn't show up in newer environments.


Status changed to Awaiting User Response Railway 2 months ago


Status changed to Solved chandrika 2 months ago


Welcome!

Sign in to your Railway account to join the conversation.

Loading...