Ansible Nginx install giving file not found error

Hello every one. I just started learning Ansible. I am trying to use ansible to install and configure nginx. My ansible
play-book installs and configures nginx alright but each time I try to launch the website I get a 404 file not found error.
The website consists of a single web page saying “Welcome to Ansible” and displaying the Ansible logo.

My playbook contents is as follows:

---
- hosts: remote
  gather_facts: False
  become: true
  tasks:
   - name: Install NGINX
     apt: pkg=nginx state=present update_cache=true
     notify:
      - start nginx
     become_user: root
   - name: copy the nginx config file and restart nginx
     copy:
       src: /home/abdul/ansible-tutorial/static_site.cfg
       dest: /etc/nginx/sites-available/static_site.cfg
     become_user: root
   - name: create symlink
     file:
       src: /etc/nginx/sites-available/static_site.cfg
       dest: /etc/nginx/sites-enabled/default
       state: link
     become_user: root
   - name: copy contents of site
     copy:
       src: /home/abdul/ansible-tutorial/static_site_src/
       dest: /home/abdul/static_site
   - name: restart nginx

The contents of my nginx config file is as follows:

server {
        listen 80 default_server;
        listen [::]:80 default_server;
        root /home/abdul/static_site;
        index index.html index.htm;
        server_name abdulnginxtest.com;
        location / {
                try_files $uri $uri/ =404;
        }
}

The content of my ansible host file is as follows:

# hosts

[remote]

host1 ansible_host=xxx.xxx.xxx.xxx ansible_user=abdul

/home/abdul/static_site is where I have the website. In it is a page index.html with the following html code:

<h1>Welcome to Ansible</h1>
<img src="/ansible_logo.png" />

(I have also tried ./ansible_logo.png)

The folder also contains the ansible logo referenced in the index.html page

I am running Windows 10 and on it I am running Windows Subsystem for Linux (WSL), which is where I have installed Ansible.
I should point out that the domain abdulnginxtest.com is not registered with any DNS provider. I am using the OS hosts to test.
The problem is that I don’t whether I should use the Windows hosts file or the WSL hosts file. Currently I have the following entry
in both of them:

xxx.xxx.xxx.xxx  abdulnginxtest.com 

I have tested both in Mozilla running off the Windows OS and the lynx browser running off the WSL. I still have the 404 Not found error.
Please I would be glad for any pointers showing what I am doing wrong. Thanks

1 Like

Hi,

At first glance, nothing inherently wrong with your playbook* or Nginx configuration, though just to be sure:

  • Did your ansible-playbook... command gave you any error ? Were there skipped tasks ? Did everything completed ? If not sure, just paste the output here
  • Is Nginx service running on your target host (host1, according to your inventory file) ?
  • Does nginx -t on host1 gives you any error ?
  • Have you checked all config files were correctly copied over on host1 ?
  • Is “/etc/nginx/sites-enabled/default” path included in your nginx.conf http{} block ?

* Except for the task - name: restart nginx at your playbook’s bottom, though I’m not sure if that’s a copy/paste issue ? You need to reload / restart Nginx daemon for your configuration to take effect, so did this task complete without issues ? Side note: you should use handlers for this kind of things :wink:

each time I try to launch the website I get a 404 file not found error

So to be clear, when you access http://abdulnginxtest.com through your http client, it returns a 404 http code. I’m wondering it this response is indeed returned by Nginx ? Could you run this command from your ansible control node (where you can resolve host1): curl -Iv http://abdulnginxtest.com, and paste output here ?

I should point out that the domain abdulnginxtest.com is not registered with any DNS provider. I am using the OS hosts to test.

Ansible supposedly managed to access host1, install packages and copy config files; nothing to see here :slight_smile:

3 Likes

Hello Pierre. Thanks so much for the detailed response:

Did your ansible-playbook... command gave you any error ? Were there
skipped tasks ? Did everything completed ? If not sure, just paste the output here

PLAY [remote] **********************************************************************************************************

TASK [Install NGINX] ***************************************************************************************************
ok: [host1]

TASK [copy the nginx config file and restart nginx] ********************************************************************
ok: [host1]

TASK [create symlink] **************************************************************************************************
ok: [host1]

TASK [copy contents of site] *******************************************************************************************
changed: [host1]

TASK [restart nginx] ***************************************************************************************************
changed: [host1]

PLAY RECAP *************************************************************************************************************
host1                      : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Is Nginx service running on your target host (host1, according to your inventory file)?

Yes

Does nginx -t on host1 gives you any error?

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Have you checked all config files were correctly copied over on host1?

My config file at /etc/nginx/sites-available/static_site.cfg was successfully copied
The symlink to /etc/nginx/sites-enabled/default was successfully created

Is “/etc/nginx/sites-enabled/default” path included in your nginx.conf http{} block?

That is the /etc/nginx/nginx.conf file right?

http {
   ....
   ....
   include /etc/nginx/sites-enabled/*;
   ....
}

Except for the task - name: restart nginx at your playbook’s bottom, though I’m not sure if
that’s a copy/paste issue ? You need to reload / restart Nginx daemon for your
configuration to take effect, so did this task complete without issues?

Oh I am very sorry. Didn’t put up the complete playbook when posting the problem on the forum though it is complete on my system. Here is the rest of it:

   - name: restart nginx
     service:
       name: nginx
       state: restarted
     become_user: root
  handlers:
  - name: start nginx
    service: name=nginx state=started
    become_user: root

So to be clear, when you access http://abdulnginxtest.com through your http client,
it returns a 404 http code. I’m wondering it this response is indeed returned by Nginx?
Could you run this command from your ansible control node (where you can resolve host1):

curl -Iv http://abdulnginxtest.com

and paste output here?

*   Trying xxx.xxx.xxx.xxx:80...
* Connected to abdulnginxtest.com (xxx.xxx.xxx.xxx) port 80 (#0)
> HEAD / HTTP/1.1
> Host: abdulnginxtest.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
HTTP/1.1 404 Not Found
< Server: nginx/1.18.0 (Ubuntu)
Server: nginx/1.18.0 (Ubuntu)
< Date: Thu, 05 Oct 2023 21:46:25 GMT
Date: Thu, 05 Oct 2023 21:46:25 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 162
Content-Length: 162
< Connection: keep-alive
Connection: keep-alive

<
* Connection #0 to host abdulnginxtest.com left intact

That is from my laptop on which ansible is installed. That would be the ansible control node right? It tries to connect to the correct server ip

1 Like

Hey,

Thanks for providing complementary info.

TASK [copy contents of site] *******************************************************************************************
changed: [host1]

I wonder why this task is in ‘changed’ status. If you haven’t done any modification on either src or dest file, it shouldn’t trigger a change. Probably not where your issue lies though.

Server: nginx/1.18.0 (Ubuntu)

First off, Nginx is receiving your request, yay ! Although this is an old version. Apparently your target host runs Ubuntu, probably 20.04. I don’t think it’s related to your issue neither.

That is from my laptop on which ansible is installed. That would be the ansible control node right?

Yup !

So, I tried to reproduce the issue on my end, and haven’t managed to do so :/.
I ran your playbook from my workstation on a locally running Debian 12 systemd container using connection type ‘docker’; here are some output so you can compare:

15:29|ptn@bender:~/TEMP/test_nginx (bg:1)$ curl -vD - http://abdulnginxtest.com
*   Trying 127.0.0.1:80...
* Connected to abdulnginxtest.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: abdulnginxtest.com
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx/1.22.1
Server: nginx/1.22.1
< Date: Sat, 07 Oct 2023 13:29:14 GMT
Date: Sat, 07 Oct 2023 13:29:14 GMT
< Content-Type: text/html
Content-Type: text/html
< Content-Length: 28
Content-Length: 28
< Last-Modified: Sat, 07 Oct 2023 13:03:01 GMT
Last-Modified: Sat, 07 Oct 2023 13:03:01 GMT
< Connection: keep-alive
Connection: keep-alive
< ETag: "65215705-1c"
ETag: "65215705-1c"
< Accept-Ranges: bytes
Accept-Ranges: bytes

< 
<h1>Welcome to Ansible</h1>
<img src="/ansible_logo.png" />
* Connection #0 to host abdulnginxtest.com left intact

15:30|ptn@bender:~/TEMP/test_nginx (bg:1)$ docker exec -it test1 tree -a /home/ansible/static_site /etc/nginx/sites-enabled/ -pug
[drwxr-xr-x root     root    ]  /home/ansible/static_site
├── [-rw-r--r-- root     root    ]  ansible_logo.png
└── [-rw-r--r-- root     root    ]  index.html
[drwxr-xr-x root     root    ]  /etc/nginx/sites-enabled/
└── [lrwxrwxrwx root     root    ]  default -> /etc/nginx/sites-available/static_site.cfg

15:30|ptn@bender:~/TEMP/test_nginx (bg:1)$ cat ./hosts
test1 ansible_user=ansible

Anyways, in this context the 404/HTTP code you’re getting is probably returned by the try_files directive because it can’t find either the $uri or $uri/ part.

I was thinking it might be because Nginx can’t resolve the hostname you put in server_name directive (abdulnginxtest.com), as it’s a remote machine which doesn’t use your /etc/hosts file, but I tried removing this hostname from my local /etc/hosts and running the HTTP HEAD/GET request from another machine and it still works. I think Nginx only cares about name resolution for upstream backend nodes, though I’m not sure.

I also wondered about permissions on remote files, though you’d get a 403/HTTP for that.

So, you could try to change the try_files return code by something else, to ensure your issue stems from that, but most importantly: logs !! Could you output /var/log/nginx/{access,error}.log after you tried accessing your website ?

Also not related to your issue but there are some improvement we can bring to your playbook; some are just best practice as using FQCN and writing your tasks in an unfolded way for readability, but the one change I’m thinking of would be to adapt your handler to have Nginx restarted instead, and notify it for every change on your config files, which would be cleaner than forcing Nginx service to restart on every playbook run (your last task). Anyways, it’s just an unsolicited advice; do as you please :slight_smile:

1 Like

Hello Pierre. Sorry for the late response. I am really grateful for the detailed responses. Please let me go through and respond accordingly. Thanks

2 Likes

Hi Pierre

I actually have been trying to respond but each time I try to reply with a post containing the relevant part of my log files. I get an error saying you cannot post a link to that host

What could be wrong?

Hey,

I don’t know for sure; it might be tied to your Discourse role. You can always paste it in gist or the online paste tool of your choice :wink:

Error log

2023/10/09 13:27:53 [crit] 1687135#1687135: *615 stat() "/home/abdul/static_site/" failed (13: Permission denied), client: 102.88.62.211, server: abdulnginxtest.com, request: "GET / HTTP/1.0", host: "abdulnginxtest.com"
2023/10/09 13:27:53 [crit] 1687135#1687135: *615 stat() "/home/abdul/static_site/" failed (13: Permission denied), client: 102.88.62.211, server: abdulnginxtest.com, request: "GET / HTTP/1.0", host: "abdulnginxtest.com"

Access log

102.88.62.211 - - [09/Oct/2023:13:27:53 +0000] "GET / HTTP/1.0" 404 162 "-" "Lynx/2.9.0dev.10 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.1"

Sorry for the terse nature of the post. The forum flagged my original response as spam. I am trying to avoid that.

Permission denied sounds like a file/directory permissions thing. The permissions on /home/abdul/static_site/ are currently set to rwxr-xr-x. That sounds okay right?

So, you could try to change the try_files return code by something…

Please I only have rudimentary knowledge of Nginx. Would you be so kind to demonstrate with an example?

Also not related to your issue but there are some improvement…

All suggestions are welcome

Well, a few things I think I can take away from your response is that I set up my play book correctly and that your replication of my set up returned the page with the Ansible logo, meaning that in my set up the problem is not with ansible but lies entirely within Nginx. Am I correct?

Sorry broke down my post into multiple parts to isolate offending parts

I actually tried twice. Once using lynx on WSL. The other using firefox running off the Windows host. Only the lynx test showed up in the access log. I wonder why?

The forum flagged my original response as spam. I am trying to avoid that.

That sucks :frowning: . I wanted to have another look on your playbook. Meh.

Permission denied sounds like a file/directory permissions thing. The permissions on /home/abdul/static_site/ are currently set to rwxr-xr-x. That sounds okay right?

Check owner:group of the files, current, and parent folders. The account running nginx processes should own those files, which also means have access on the complete path. It’s usually root, at least for master process, so files access should be fine, but I’m wondering who owns those files.

I wanted to check again your “copy contents of site” task, see if you also have the ansible_user=root directive defined on this one. On my end, I removed these as I’m connecting with another user, and have become: true on play level, which by default run tasks as root using sudo, so no need to specify it except if you had to connect with root prior to privilege escalation.
Just checked and you had become_user: root, not ansible_user defined on your tasks, except for the one you use to deploy your static site files. So not the same thing, but it shouldn’t matter except if you defined another user for become_user somewhere else. In doubt, try to add become_user: root to this task and inspect copied files for a potential ownership change.

If I’m not missing something, it shouldn’t change anything, though you might want to specify ownership parameters for copied files on task args as precaution:

 - name: copy contents of site                                                                                                                             
      copy:                                                                                                                                                   
        src: /home/ptn/TEMP/test_nginx/static_site_src/                                                                                                       
        dest: /home/ansible/static_site
        owner: root
        group: root

Could you run this command and paste the output: tree -augp /home/abdul/static_site/ on your nginx node ?

Please I only have rudimentary knowledge of Nginx. Would you be so kind to demonstrate with an example?

I’m sorry. I was suggesting you change this line from your vhost config (static_site.cfg here): try_files $uri $uri/ =404; to try_files $uri $uri/ =<anotherHttpCode>; (without ‘<’ and ‘>’). This way, you could tell if the 404/HTTP returned code is a legit 404 or the one you set up on your try_files directive.
And now we know you get a 404 because Nginx can’t access $uri or files under.

All suggestions are welcome

<3

Well, a few things I think I can take away from your response is that I set up my play book correctly and that your replication of my set up returned the page with the Ansible logo, meaning that in my set up the problem is not with ansible but lies entirely within Nginx. Am I correct?

Yes. Well, not really Nginx as your issue is about files access, though Ansible did its job here.

I actually tried twice. Once using lynx on WSL. The other using firefox running off the Windows host. Only the lynx test showed up in the access log. I wonder why?

AFAIK default behavior is: HTTP clients request are logs in access.log and errors (not requests) only in error.log. They are not mutually exclusive, a failed request will still appear in access.log, and related errors will be in error.log. Though you can change log format and such.

Now you mention requests from a Firefox browser on your Windows host and it seems to me your Windows host couldn’t join your Nginx instance. I don’t remember well enough your name resolution config, but it might be possible you only added your website hostname in WSL /etc/hosts file and not in the counterpart file on your Windows machine.
IIRC, WSL /etc/hosts file is by default generated from Windows config (I might be wrong, I haven’t used WSL for a long time), but the opposite is not true.

Anyways, keep me posted !

Hello Pierre. I found the problem Yay!!!
It was the permissions on my home folder /home/abdul.
They were set to rwxr-x—.
Once I changed to rwxr-xr-x, it worked!!

Thank you so much for all the help and sorry for all the trouble.

1 Like

Now that the permission issues are rectified, it actually comes up in both lynx and firefox.

:partying_face:

They were set to rwxr-x—.
Once I changed to rwxr-xr-x, it worked!!

It still is kinda weird. Nginx master process’s user (defaults as root) needs path traversal access to your website files, and AFAIK be owner of those files, though it seems it didn’t have access to your home (since you added RX permissions to others). Weird ! Or I’m being rusted in GNU/Linux admin, which might well be the case :/.

Anyways, you should probably deploy those files elsewhere, like /usr, /opt, /etc or /var, while specifying owner:group and ACL on your copy task.

Next step: Deploying your conf from templates ? :wink:

Glad you fixed your issue; have fun with Ansible !

Anyways, you should probably deploy those files elsewhere, like…

Okay

Next step: Deploying your conf from templates ?..

lol…I don’t know…possibly…be sure to give it a look though

Glad you fixed your issue; have fun with Ansible

Thanks again Pierre. Will keep you posted on my Ansible journey. Cheers

1 Like

For the record, that’s because of the number of links posted by a brand new user. Spammers do link to their own domains, so when a new users repeatedly links to a new domain, the detection systems will flag it for moderator approval (which we clearly approved in this case).

I’ve also added a couple of the more commonly-used domains to the whitelist so that people get hit with this less. Thanks for persevering!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.